func TestRun(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mockOR := pipelinemock.NewMockOutputRunner(mockCtrl) mockPH := pipelinemock.NewMockPluginHelper(mockCtrl) mockFirehose := NewMockRecordPutter(mockCtrl) firehoseOutput := FirehoseOutput{ client: mockFirehose, } testChan := make(chan *pipeline.PipelinePack) mockOR.EXPECT().InChan().Return(testChan) // Send test input through the channel input := `{"key":"value"}` go func() { testPack := pipeline.PipelinePack{ Message: &message.Message{ Payload: &input, }, } testChan <- &testPack close(testChan) }() mockFirehose.EXPECT().PutRecord([]byte(input)).Return(nil) err := firehoseOutput.Run(mockOR, mockPH) assert.NoError(t, err, "did not expect err for valid Run()") }
func HttpListenInputSpec(c gs.Context) { t := &pipeline_ts.SimpleT{} ctrl := gomock.NewController(t) defer ctrl.Finish() pConfig := NewPipelineConfig(nil) httpListenInput := HttpListenInput{} ith := new(plugins_ts.InputTestHelper) ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) startInput := func() { go func() { httpListenInput.Run(ith.MockInputRunner, ith.MockHelper) }() } ith.Pack = NewPipelinePack(pConfig.InputRecycleChan()) ith.PackSupply = make(chan *PipelinePack, 1) config := httpListenInput.ConfigStruct().(*HttpListenInputConfig) config.Address = "127.0.0.1:8325" config.Decoder = "PayloadJsonDecoder" ith.MockHelper.EXPECT().PipelineConfig().Return(pConfig) mockDecoderRunner := pipelinemock.NewMockDecoderRunner(ctrl) dRunnerInChan := make(chan *PipelinePack, 1) mockDecoderRunner.EXPECT().InChan().Return(dRunnerInChan).AnyTimes() ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply).AnyTimes() ith.MockInputRunner.EXPECT().Name().Return("HttpListenInput").AnyTimes() ith.MockHelper.EXPECT().DecoderRunner("PayloadJsonDecoder", "HttpListenInput-PayloadJsonDecoder").Return(mockDecoderRunner, true) err := httpListenInput.Init(config) c.Assume(err, gs.IsNil) ith.MockInputRunner.EXPECT().LogMessage(gomock.Any()) startInput() c.Specify("A HttpListenInput", func() { c.Specify("Adds query parameters to the message pack as fields", func() { ith.PackSupply <- ith.Pack resp, err := http.Get("http://127.0.0.1:8325/?test=Hello%20World") c.Assume(err, gs.IsNil) resp.Body.Close() c.Assume(resp.StatusCode, gs.Equals, 200) pack := <-dRunnerInChan fieldValue, ok := pack.Message.GetFieldValue("test") c.Assume(ok, gs.IsTrue) c.Expect(fieldValue, gs.Equals, "Hello World") }) httpListenInput.Stop() }) }
// TestRunWithTimestamp tests that if a TimestampColumn is provided in the config // then the Heka message's timestamp gets added to the message with that column name. func TestRunWithTimestamp(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mockOR := pipelinemock.NewMockOutputRunner(mockCtrl) mockPH := pipelinemock.NewMockPluginHelper(mockCtrl) mockFirehose := NewMockRecordPutter(mockCtrl) firehoseOutput := FirehoseOutput{ client: mockFirehose, timestampColumn: "created", } testChan := make(chan *pipeline.PipelinePack) mockOR.EXPECT().InChan().Return(testChan) // Send test input through the channel input := `{}` timestamp := time.Date(2015, 07, 1, 13, 14, 15, 0, time.UTC).UnixNano() go func() { testPack := pipeline.PipelinePack{ Message: &message.Message{ Payload: &input, Timestamp: ×tamp, }, } testChan <- &testPack close(testChan) }() expected := `{"created":"2015-07-01 13:14:15.000"}` mockFirehose.EXPECT().PutRecord([]byte(expected)).Return(nil) err := firehoseOutput.Run(mockOR, mockPH) assert.NoError(t, err, "did not expect err for valid Run()") }
func WhisperOutputSpec(c gospec.Context) { t := &pipeline_ts.SimpleT{} ctrl := gomock.NewController(t) defer ctrl.Finish() oth := new(plugins_ts.OutputTestHelper) oth.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) oth.MockOutputRunner = pipelinemock.NewMockOutputRunner(ctrl) inChan := make(chan *PipelinePack, 1) pConfig := NewPipelineConfig(nil) c.Specify("A WhisperOutput", func() { o := &WhisperOutput{pConfig: pConfig} config := o.ConfigStruct().(*WhisperOutputConfig) config.BasePath = filepath.Join(os.TempDir(), config.BasePath) o.Init(config) const count = 5 lines := make([]string, count) baseTime := time.Now().UTC().Add(-10 * time.Second) nameTmpl := "stats.name.%d" wChan := make(chan *whisper.Point, count) mockWr := NewMockWhisperRunner(ctrl) for i := 0; i < count; i++ { statName := fmt.Sprintf(nameTmpl, i) statTime := baseTime.Add(time.Duration(i) * time.Second) lines[i] = fmt.Sprintf("%s %d %d", statName, i*2, statTime.Unix()) o.dbs[statName] = mockWr } pack := NewPipelinePack(pConfig.InputRecycleChan()) pack.Message.SetPayload(strings.Join(lines, "\n")) c.Specify("turns statmetric lines into points", func() { oth.MockOutputRunner.EXPECT().InChan().Return(inChan) oth.MockOutputRunner.EXPECT().UpdateCursor("") wChanCall := mockWr.EXPECT().InChan().Times(count) wChanCall.Return(wChan) go o.Run(oth.MockOutputRunner, oth.MockHelper) inChan <- pack // Usually each wChan will be unique instead of shared across // multiple whisper runners. This weird dance here prevents our // mock wChan from being closed multiple times. bogusChan := make(chan *whisper.Point) wChanCall = mockWr.EXPECT().InChan().Times(count) wChanCall.Return(bogusChan) wChanCall.Do(func() { wChanCall.Return(make(chan *whisper.Point)) }) close(inChan) <-bogusChan // wait for inChan to flush close(wChan) i := 0 for pt := range wChan { statTime := baseTime.Add(time.Duration(i) * time.Second) c.Expect(pt.Value, gs.Equals, float64(i*2)) c.Expect(pt.Time().UTC().Unix(), gs.Equals, statTime.Unix()) i++ } }) }) }
func NewFilterTestHelper(t mock.TestingT, ctrl *gomock.Controller) *FilterTestHelper { fth := new(FilterTestHelper) fth.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) fth.MockFilterRunner = &MockFilterRunner{mock.Mock{}, t} return fth }
func UdpInputSpec(c gs.Context) { t := &pipeline_ts.SimpleT{} ctrl := gomock.NewController(t) defer ctrl.Finish() config := NewPipelineConfig(nil) ith := new(plugins_ts.InputTestHelper) ith.Msg = pipeline_ts.GetTestMessage() ith.Pack = NewPipelinePack(config.InputRecycleChan()) ith.AddrStr = "localhost:55565" ith.ResolvedAddrStr = "127.0.0.1:55565" // set up mock helper, decoder set, and packSupply channel ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) ith.Decoder = pipelinemock.NewMockDecoderRunner(ctrl) ith.PackSupply = make(chan *PipelinePack, 1) ith.DecodeChan = make(chan *PipelinePack) c.Specify("A UdpInput", func() { udpInput := UdpInput{} err := udpInput.Init(&UdpInputConfig{Net: "udp", Address: ith.AddrStr, Decoder: "ProtobufDecoder", ParserType: "message.proto"}) c.Assume(err, gs.IsNil) realListener := (udpInput.listener).(*net.UDPConn) c.Expect(realListener.LocalAddr().String(), gs.Equals, ith.ResolvedAddrStr) mbytes, _ := proto.Marshal(ith.Msg) header := &message.Header{} header.SetMessageLength(uint32(len(mbytes))) mockDecoderRunner := ith.Decoder.(*pipelinemock.MockDecoderRunner) mockDecoderRunner.EXPECT().InChan().Return(ith.DecodeChan) ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply) ith.MockInputRunner.EXPECT().Name().Return("UdpInput") encCall := ith.MockHelper.EXPECT().DecoderRunner("ProtobufDecoder", "UdpInput-ProtobufDecoder") encCall.Return(ith.Decoder, true) c.Specify("reads a message from the connection and passes it to the decoder", func() { hbytes, _ := proto.Marshal(header) go func() { udpInput.Run(ith.MockInputRunner, ith.MockHelper) }() conn, err := net.Dial("udp", ith.AddrStr) // a mock connection will not work here since the mock read cannot block c.Assume(err, gs.IsNil) buf := encodeMessage(hbytes, mbytes) _, err = conn.Write(buf) c.Assume(err, gs.IsNil) ith.PackSupply <- ith.Pack packRef := <-ith.DecodeChan udpInput.Stop() c.Expect(ith.Pack, gs.Equals, packRef) c.Expect(string(ith.Pack.MsgBytes), gs.Equals, string(mbytes)) c.Expect(ith.Pack.Decoded, gs.IsFalse) }) }) c.Specify("A UdpInput Multiline input", func() { ith.AddrStr = "localhost:55566" ith.ResolvedAddrStr = "127.0.0.1:55566" udpInput := UdpInput{} err := udpInput.Init(&UdpInputConfig{Net: "udp", Address: ith.AddrStr, Decoder: "test", ParserType: "token"}) c.Assume(err, gs.IsNil) realListener := (udpInput.listener).(*net.UDPConn) c.Expect(realListener.LocalAddr().String(), gs.Equals, ith.ResolvedAddrStr) mockDecoderRunner := ith.Decoder.(*pipelinemock.MockDecoderRunner) mockDecoderRunner.EXPECT().InChan().Return(ith.DecodeChan).Times(2) ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply).Times(2) ith.MockInputRunner.EXPECT().Name().Return("UdpInput").AnyTimes() encCall := ith.MockHelper.EXPECT().DecoderRunner("test", "UdpInput-test") encCall.Return(ith.Decoder, true) c.Specify("reads two messages from a packet and passes them to the decoder", func() { go func() { udpInput.Run(ith.MockInputRunner, ith.MockHelper) }() conn, err := net.Dial("udp", ith.AddrStr) // a mock connection will not work here since the mock read cannot block c.Assume(err, gs.IsNil) _, err = conn.Write([]byte("message1\nmessage2\n")) c.Assume(err, gs.IsNil) ith.PackSupply <- ith.Pack packRef := <-ith.DecodeChan c.Expect(string(packRef.Message.GetPayload()), gs.Equals, "message1\n") ith.PackSupply <- ith.Pack packRef = <-ith.DecodeChan c.Expect(string(packRef.Message.GetPayload()), gs.Equals, "message2\n") udpInput.Stop() }) }) }
func NsqInputSpec(c gs.Context) { t := new(pipeline_ts.SimpleT) ctrl := gomock.NewController(t) defer ctrl.Finish() pConfig := pipeline.NewPipelineConfig(nil) var wg sync.WaitGroup errChan := make(chan error, 1) retPackChan := make(chan *pipeline.PipelinePack, 1) defer close(errChan) defer close(retPackChan) c.Specify("A nsq input", func() { input := new(NsqInput) ith := new(plugins_ts.InputTestHelper) ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) config := input.ConfigStruct().(*NsqInputConfig) config.Topic = "test_topic" config.Channel = "test_channel" startInput := func() { wg.Add(1) go func() { errChan <- input.Run(ith.MockInputRunner, ith.MockHelper) wg.Done() }() } var mockConsumer *MockConsumer input.newConsumer = func(topic, channel string, config *nsq.Config) (Consumer, error) { mockConsumer, _ = NewMockConsumer(topic, channel, config) return mockConsumer, nil } ith.Pack = pipeline.NewPipelinePack(pConfig.InputRecycleChan()) ith.PackSupply = make(chan *pipeline.PipelinePack, 1) ith.PackSupply <- ith.Pack inputName := "NsqInput" ith.MockInputRunner.EXPECT().Name().Return(inputName).AnyTimes() ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply) c.Specify("that is started", func() { input.SetPipelineConfig(pConfig) c.Specify("gets messages its subscribed to", func() { c.Specify("injects messages into the pipeline when not configured with a decoder", func() { ith.MockInputRunner.EXPECT().Inject(ith.Pack).Do(func(p *pipeline.PipelinePack) { retPackChan <- p }).AnyTimes() err := input.Init(config) c.Expect(err, gs.IsNil) c.Expect(input.DecoderName, gs.Equals, "") }) c.Specify("injects messages into the decoder when configured", func() { decoderName := "ScribbleDecoder" config.DecoderName = decoderName decoder := new(plugins.ScribbleDecoder) decoder.Init(&plugins.ScribbleDecoderConfig{}) mockDecoderRunner := pipelinemock.NewMockDecoderRunner(ctrl) mockDecoderRunner.EXPECT().InChan().Return(retPackChan) ith.MockHelper.EXPECT().DecoderRunner(decoderName, fmt.Sprintf("%s-%s", inputName, decoderName), ).Return(mockDecoderRunner, true) err := input.Init(config) c.Expect(err, gs.IsNil) c.Expect(input.DecoderName, gs.Equals, decoderName) }) startInput() // Should get two finished conn calls since we connect // to lookupds and nsqds c.Expect(<-mockConsumer.finishedConn, gs.IsTrue) c.Expect(<-mockConsumer.finishedConn, gs.IsTrue) id := nsq.MessageID{} body := []byte("test") msg := nsq.NewMessage(id, body) for _, handler := range mockConsumer.handlers { handler.HandleMessage(msg) } p := <-retPackChan c.Expect(p.Message.GetPayload(), gs.Equals, "test") }) input.Stop() wg.Wait() c.Expect(<-errChan, gs.IsNil) }) }) }
func LogfileInputSpec1(c gs.Context) { t := &pipeline_ts.SimpleT{} ctrl := gomock.NewController(t) defer ctrl.Finish() config := NewPipelineConfig(nil) tmpDir, tmpErr := ioutil.TempDir("", "hekad-tests-") c.Expect(tmpErr, gs.Equals, nil) origBaseDir := Globals().BaseDir Globals().BaseDir = tmpDir defer func() { Globals().BaseDir = origBaseDir tmpErr = os.RemoveAll(tmpDir) c.Expect(tmpErr, gs.Equals, nil) }() journalName := "test-seekjournal" journalDir := filepath.Join(tmpDir, "seekjournal") tmpErr = os.MkdirAll(journalDir, 0770) c.Expect(tmpErr, gs.Equals, nil) ith := new(plugins_ts.InputTestHelper) ith.Msg = pipeline_ts.GetTestMessage() ith.Pack = NewPipelinePack(config.InputRecycleChan()) // Specify localhost, but we're not really going to use the network ith.AddrStr = "localhost:55565" ith.ResolvedAddrStr = "127.0.0.1:55565" // set up mock helper, decoder set, and packSupply channel ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) ith.PackSupply = make(chan *PipelinePack, 1) ith.DecodeChan = make(chan *PipelinePack) c.Specify("A LogfileInput", func() { c.Specify("save the seek position of the last complete logline", func() { lfInput, lfiConfig := createIncompleteLogfileInput(journalName) // Initialize the input test helper err := lfInput.Init(lfiConfig) c.Expect(err, gs.IsNil) dName := "decoder-name" lfInput.decoderName = dName mockDecoderRunner := pipelinemock.NewMockDecoderRunner(ctrl) // Create pool of packs. numLines := 4 // # of lines in the log file we're parsing. packs := make([]*PipelinePack, numLines) ith.PackSupply = make(chan *PipelinePack, numLines) for i := 0; i < numLines; i++ { packs[i] = NewPipelinePack(ith.PackSupply) ith.PackSupply <- packs[i] } // Expect InputRunner calls to get InChan and inject outgoing msgs ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply).Times(numLines) // Expect calls to get decoder and decode each message. Since the // decoding is a no-op, the message payload will be the log file // line, unchanged. ith.MockInputRunner.EXPECT().Name().Return("LogfileInput") pbcall := ith.MockHelper.EXPECT().DecoderRunner(dName, "LogfileInput-"+dName) pbcall.Return(mockDecoderRunner, true) decodeCall := mockDecoderRunner.EXPECT().InChan().Times(numLines) decodeCall.Return(ith.DecodeChan) go func() { err = lfInput.Run(ith.MockInputRunner, ith.MockHelper) c.Expect(err, gs.IsNil) }() for x := 0; x < numLines; x++ { _ = <-ith.DecodeChan // Free up the scheduler while we wait for the log file lines // to be processed. runtime.Gosched() } newFM := new(FileMonitor) newFM.Init(lfiConfig) c.Expect(err, gs.Equals, nil) fbytes, _ := json.Marshal(lfInput.Monitor) // Check that the persisted hashcode is from the last // complete log line expected_lastline := `10.1.1.4 plinko-565.byzantium.mozilla.com user3 [15/Mar/2013:12:20:27 -0700] "GET /1.1/user3/storage/passwords?newer=1356237662.44&full=1 HTTP/1.1" 200 1396 "-" "Firefox/20.0.1 FxSync/1.22.0.201304.desktop" "-" "ssl: SSL_RSA_WITH_RC4_128_SHA, version=TLSv1, bits=128" node_s:0.047167 req_s:0.047167 retries:0 req_b:446 "c_l:-"` + "\n" c.Expect((strings.IndexAny(string(fbytes), sha1_hexdigest(expected_lastline)) > -1), gs.IsTrue) json.Unmarshal(fbytes, &newFM) if runtime.GOOS == "windows" { c.Expect(newFM.seek, gs.Equals, int64(1253)) } else { c.Expect(newFM.seek, gs.Equals, int64(1249)) } lfInput.Stop() }) }) c.Specify("A LogfileDirectoryManagerInput", func() { c.Specify("empty file name", func() { var err error ldm := new(LogfileDirectoryManagerInput) conf := ldm.ConfigStruct().(*LogfileInputConfig) conf.LogFile = "" err = ldm.Init(conf) c.Expect(err, gs.Not(gs.IsNil)) c.Expect(err.Error(), gs.Equals, "A logfile name must be specified.") }) c.Specify("glob in file name", func() { var err error ldm := new(LogfileDirectoryManagerInput) conf := ldm.ConfigStruct().(*LogfileInputConfig) conf.LogFile = "../testsupport/*.log" err = ldm.Init(conf) c.Expect(err, gs.Not(gs.IsNil)) c.Expect(err.Error(), gs.Equals, "Globs are not allowed in the file name: *.log") }) // Note: Testing the actual functionality (spinning up new plugins within Heka) // is a manual process. }) }
func FilePollingInputSpec(c gs.Context) { t := new(pipeline_ts.SimpleT) ctrl := gomock.NewController(t) tmpFileName := fmt.Sprintf("filepollinginput-test-%d", time.Now().UnixNano()) tmpFilePath := filepath.Join(os.TempDir(), tmpFileName) defer func() { ctrl.Finish() os.Remove(tmpFilePath) }() pConfig := NewPipelineConfig(nil) var wg sync.WaitGroup errChan := make(chan error, 1) bytesChan := make(chan []byte, 1) tickChan := make(chan time.Time) retPackChan := make(chan *PipelinePack, 2) defer close(retPackChan) c.Specify("A FilePollingInput", func() { input := new(FilePollingInput) ith := new(plugins_ts.InputTestHelper) ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) ith.MockSplitterRunner = pipelinemock.NewMockSplitterRunner(ctrl) config := input.ConfigStruct().(*FilePollingInputConfig) config.FilePath = tmpFilePath startInput := func(msgCount int) { wg.Add(1) go func() { errChan <- input.Run(ith.MockInputRunner, ith.MockHelper) wg.Done() }() } ith.MockInputRunner.EXPECT().Ticker().Return(tickChan) ith.MockHelper.EXPECT().PipelineConfig().Return(pConfig) c.Specify("gets updated information when reading a file", func() { err := input.Init(config) c.Assume(err, gs.IsNil) ith.MockInputRunner.EXPECT().NewSplitterRunner("").Return(ith.MockSplitterRunner) ith.MockSplitterRunner.EXPECT().UseMsgBytes().Return(false) ith.MockSplitterRunner.EXPECT().SetPackDecorator(gomock.Any()) splitCall := ith.MockSplitterRunner.EXPECT().SplitStream(gomock.Any(), nil).Return(io.EOF).Times(2) splitCall.Do(func(f *os.File, del Deliverer) { fBytes, err := ioutil.ReadAll(f) if err != nil { fBytes = []byte(err.Error()) } bytesChan <- fBytes }) startInput(2) f, err := os.Create(tmpFilePath) c.Expect(err, gs.IsNil) _, err = f.Write([]byte("test1")) c.Expect(err, gs.IsNil) c.Expect(f.Close(), gs.IsNil) tickChan <- time.Now() msgBytes := <-bytesChan c.Expect(string(msgBytes), gs.Equals, "test1") f, err = os.Create(tmpFilePath) c.Expect(err, gs.IsNil) _, err = f.Write([]byte("test2")) c.Expect(err, gs.IsNil) c.Expect(f.Close(), gs.IsNil) tickChan <- time.Now() msgBytes = <-bytesChan c.Expect(string(msgBytes), gs.Equals, "test2") input.Stop() wg.Wait() c.Expect(<-errChan, gs.IsNil) }) }) }
func HttpInputSpec(c gs.Context) { t := &pipeline_ts.SimpleT{} ctrl := gomock.NewController(t) defer ctrl.Finish() pConfig := NewPipelineConfig(nil) json_post := `{"uuid": "xxBI3zyeXU+spG8Uiveumw==", "timestamp": 1372966886023588, "hostname": "Victors-MacBook-Air.local", "pid": 40183, "fields": [{"representation": "", "value_type": "STRING", "name": "cef_meta.syslog_priority", "value_string": [""]}, {"representation": "", "value_type": "STRING", "name": "cef_meta.syslog_ident", "value_string": [""]}, {"representation": "", "value_type": "STRING", "name": "cef_meta.syslog_facility", "value_string": [""]}, {"representation": "", "value_type": "STRING", "name": "cef_meta.syslog_options", "value_string": [""]}], "logger": "", "env_version": "0.8", "type": "cef", "payload": "Jul 04 15:41:26 Victors-MacBook-Air.local CEF:0|mozilla|weave|3|xx\\\\|x|xx\\\\|x|5|cs1Label=requestClientApplication cs1=MySuperBrowser requestMethod=GET request=/ src=127.0.0.1 dest=127.0.0.1 suser=none", "severity": 6}'` c.Specify("A HttpInput", func() { httpInput := HttpInput{} ith := new(plugins_ts.InputTestHelper) ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) c.Specify("honors time ticker to flush", func() { startInput := func() { go func() { err := httpInput.Run(ith.MockInputRunner, ith.MockHelper) c.Expect(err, gs.IsNil) }() } ith.Pack = NewPipelinePack(pConfig.InputRecycleChan()) ith.PackSupply = make(chan *PipelinePack, 1) ith.PackSupply <- ith.Pack // Spin up a http server server, err := plugins_ts.NewOneHttpServer(json_post, "localhost", 9876) c.Expect(err, gs.IsNil) go server.Start("/") time.Sleep(10 * time.Millisecond) config := httpInput.ConfigStruct().(*HttpInputConfig) decoderName := "TestDecoder" config.DecoderName = decoderName config.Url = "http://localhost:9876/" tickChan := make(chan time.Time) ith.MockInputRunner.EXPECT().LogMessage(gomock.Any()).Times(2) ith.MockHelper.EXPECT().PipelineConfig().Return(pConfig) ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply) ith.MockInputRunner.EXPECT().Ticker().Return(tickChan) mockDecoderRunner := pipelinemock.NewMockDecoderRunner(ctrl) // Stub out the DecoderRunner input channel so that we can // inspect bytes later on dRunnerInChan := make(chan *PipelinePack, 1) mockDecoderRunner.EXPECT().InChan().Return(dRunnerInChan) ith.MockInputRunner.EXPECT().Name().Return("HttpInput") ith.MockHelper.EXPECT().DecoderRunner(decoderName, "HttpInput-TestDecoder").Return(mockDecoderRunner, true) err = httpInput.Init(config) c.Assume(err, gs.IsNil) startInput() tickChan <- time.Now() // We need for the pipeline to finish up time.Sleep(50 * time.Millisecond) }) c.Specify("short circuits packs into the router", func() { startInput := func() { go func() { err := httpInput.Run(ith.MockInputRunner, ith.MockHelper) c.Expect(err, gs.IsNil) }() } ith.Pack = NewPipelinePack(pConfig.InputRecycleChan()) ith.PackSupply = make(chan *PipelinePack, 1) ith.PackSupply <- ith.Pack config := httpInput.ConfigStruct().(*HttpInputConfig) config.Url = "http://localhost:9876/" tickChan := make(chan time.Time) ith.MockInputRunner.EXPECT().LogMessage(gomock.Any()).Times(2) ith.MockHelper.EXPECT().PipelineConfig().Return(pConfig) ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply) ith.MockInputRunner.EXPECT().Ticker().Return(tickChan) err := httpInput.Init(config) c.Assume(err, gs.IsNil) startInput() tickChan <- time.Now() // We need for the pipeline to finish up time.Sleep(50 * time.Millisecond) }) c.Specify("supports configuring HTTP Basic Authentication", func() { startInput := func() { go func() { err := httpInput.Run(ith.MockInputRunner, ith.MockHelper) c.Expect(err, gs.IsNil) }() } ith.Pack = NewPipelinePack(pConfig.InputRecycleChan()) ith.PackSupply = make(chan *PipelinePack, 1) ith.PackSupply <- ith.Pack // Spin up a http server which expects username "user" and password "password" server, err := plugins_ts.NewHttpBasicAuthServer("user", "password", "localhost", 9875) c.Expect(err, gs.IsNil) go server.Start("/BasicAuthTest") time.Sleep(10 * time.Millisecond) config := httpInput.ConfigStruct().(*HttpInputConfig) decoderName := "TestDecoder" config.DecoderName = decoderName config.Url = "http://localhost:9875/BasicAuthTest" config.User = "******" config.Password = "******" tickChan := make(chan time.Time) ith.MockInputRunner.EXPECT().LogMessage(gomock.Any()).Times(2) ith.MockHelper.EXPECT().PipelineConfig().Return(pConfig) ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply) ith.MockInputRunner.EXPECT().Ticker().Return(tickChan) mockDecoderRunner := pipelinemock.NewMockDecoderRunner(ctrl) // Stub out the DecoderRunner input channel so that we can // inspect bytes later on dRunnerInChan := make(chan *PipelinePack, 1) mockDecoderRunner.EXPECT().InChan().Return(dRunnerInChan) ith.MockInputRunner.EXPECT().Name().Return("HttpInput") ith.MockHelper.EXPECT().DecoderRunner(decoderName, "HttpInput-TestDecoder").Return(mockDecoderRunner, true) err = httpInput.Init(config) c.Assume(err, gs.IsNil) startInput() tickChan <- time.Now() // we expect a statuscode 200 (i.e. success) pack := <-dRunnerInChan statusCode, ok := pack.Message.GetFieldValue("StatusCode") c.Assume(ok, gs.IsTrue) c.Expect(statusCode, gs.Equals, int64(200)) // We need for the pipeline to finish up time.Sleep(50 * time.Millisecond) }) c.Specify("supports configuring a different HTTP method", func() { startInput := func() { go func() { err := httpInput.Run(ith.MockInputRunner, ith.MockHelper) c.Expect(err, gs.IsNil) }() } ith.Pack = NewPipelinePack(pConfig.InputRecycleChan()) ith.PackSupply = make(chan *PipelinePack, 1) ith.PackSupply <- ith.Pack // Spin up a http server which expects requests with method "POST" server, err := plugins_ts.NewHttpMethodServer("POST", "localhost", 9874) c.Expect(err, gs.IsNil) go server.Start("/PostTest") time.Sleep(10 * time.Millisecond) config := httpInput.ConfigStruct().(*HttpInputConfig) decoderName := "TestDecoder" config.DecoderName = decoderName config.Url = "http://localhost:9874/PostTest" config.Method = "POST" tickChan := make(chan time.Time) ith.MockInputRunner.EXPECT().LogMessage(gomock.Any()).Times(2) ith.MockHelper.EXPECT().PipelineConfig().Return(pConfig) ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply) ith.MockInputRunner.EXPECT().Ticker().Return(tickChan) mockDecoderRunner := pipelinemock.NewMockDecoderRunner(ctrl) // Stub out the DecoderRunner input channel so that we can // inspect bytes later on dRunnerInChan := make(chan *PipelinePack, 1) mockDecoderRunner.EXPECT().InChan().Return(dRunnerInChan) ith.MockInputRunner.EXPECT().Name().Return("HttpInput") ith.MockHelper.EXPECT().DecoderRunner(decoderName, "HttpInput-TestDecoder").Return(mockDecoderRunner, true) err = httpInput.Init(config) c.Assume(err, gs.IsNil) startInput() tickChan <- time.Now() // we expect a statuscode 200 (i.e. success) pack := <-dRunnerInChan statusCode, ok := pack.Message.GetFieldValue("StatusCode") c.Assume(ok, gs.IsTrue) c.Expect(statusCode, gs.Equals, int64(200)) // We need for the pipeline to finish up time.Sleep(50 * time.Millisecond) }) c.Specify("supports configuring HTTP headers", func() { startInput := func() { go func() { err := httpInput.Run(ith.MockInputRunner, ith.MockHelper) c.Expect(err, gs.IsNil) }() } ith.Pack = NewPipelinePack(pConfig.InputRecycleChan()) ith.PackSupply = make(chan *PipelinePack, 1) ith.PackSupply <- ith.Pack // Spin up a http server which expects requests with method "POST" server, err := plugins_ts.NewHttpHeadersServer(map[string]string{"Accept": "text/plain"}, "localhost", 9873) c.Expect(err, gs.IsNil) go server.Start("/HeadersTest") time.Sleep(10 * time.Millisecond) config := httpInput.ConfigStruct().(*HttpInputConfig) decoderName := "TestDecoder" config.DecoderName = decoderName config.Url = "http://localhost:9873/HeadersTest" config.Headers = map[string]string{"Accept": "text/plain"} tickChan := make(chan time.Time) ith.MockInputRunner.EXPECT().LogMessage(gomock.Any()).Times(2) ith.MockHelper.EXPECT().PipelineConfig().Return(pConfig) ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply) ith.MockInputRunner.EXPECT().Ticker().Return(tickChan) mockDecoderRunner := pipelinemock.NewMockDecoderRunner(ctrl) // Stub out the DecoderRunner input channel so that we can // inspect bytes later on dRunnerInChan := make(chan *PipelinePack, 1) mockDecoderRunner.EXPECT().InChan().Return(dRunnerInChan) ith.MockInputRunner.EXPECT().Name().Return("HttpInput") ith.MockHelper.EXPECT().DecoderRunner(decoderName, "HttpInput-TestDecoder").Return(mockDecoderRunner, true) err = httpInput.Init(config) c.Assume(err, gs.IsNil) startInput() tickChan <- time.Now() // we expect a statuscode 200 (i.e. success) pack := <-dRunnerInChan statusCode, ok := pack.Message.GetFieldValue("StatusCode") c.Assume(ok, gs.IsTrue) c.Expect(statusCode, gs.Equals, int64(200)) // We need for the pipeline to finish up time.Sleep(50 * time.Millisecond) }) c.Specify("supports configuring a request body", func() { startInput := func() { go func() { err := httpInput.Run(ith.MockInputRunner, ith.MockHelper) c.Expect(err, gs.IsNil) }() } ith.Pack = NewPipelinePack(pConfig.InputRecycleChan()) ith.PackSupply = make(chan *PipelinePack, 1) ith.PackSupply <- ith.Pack // Spin up a http server that echoes back the request body server, err := plugins_ts.NewHttpBodyServer("localhost", 9872) c.Expect(err, gs.IsNil) go server.Start("/BodyTest") time.Sleep(10 * time.Millisecond) config := httpInput.ConfigStruct().(*HttpInputConfig) decoderName := "TestDecoder" config.DecoderName = decoderName config.Url = "http://localhost:9872/BodyTest" config.Method = "POST" config.Body = json_post tickChan := make(chan time.Time) ith.MockInputRunner.EXPECT().LogMessage(gomock.Any()).Times(2) ith.MockHelper.EXPECT().PipelineConfig().Return(pConfig) ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply) ith.MockInputRunner.EXPECT().Ticker().Return(tickChan) mockDecoderRunner := pipelinemock.NewMockDecoderRunner(ctrl) // Stub out the DecoderRunner input channel so that we can // inspect bytes later on dRunnerInChan := make(chan *PipelinePack, 1) mockDecoderRunner.EXPECT().InChan().Return(dRunnerInChan) ith.MockInputRunner.EXPECT().Name().Return("HttpInput") ith.MockHelper.EXPECT().DecoderRunner(decoderName, "HttpInput-TestDecoder").Return(mockDecoderRunner, true) err = httpInput.Init(config) c.Assume(err, gs.IsNil) startInput() tickChan <- time.Now() pack := <-dRunnerInChan c.Expect(*pack.Message.Payload, gs.Equals, json_post) // We need for the pipeline to finish up time.Sleep(50 * time.Millisecond) }) ith.MockInputRunner.EXPECT().LogMessage(gomock.Any()) httpInput.Stop() runtime.Gosched() // Yield so the stop can happen before we return. time.Sleep(50 * time.Millisecond) }) }
func LogstreamerInputSpec(c gs.Context) { t := &pipeline_ts.SimpleT{} ctrl := gomock.NewController(t) defer ctrl.Finish() here, _ := os.Getwd() dirPath := filepath.Join(here, "../../logstreamer", "testdir", "filehandling/subdir") tmpDir, tmpErr := ioutil.TempDir("", "hekad-tests") c.Expect(tmpErr, gs.Equals, nil) defer func() { tmpErr = os.RemoveAll(tmpDir) c.Expect(tmpErr, gs.IsNil) }() globals := DefaultGlobals() globals.BaseDir = tmpDir pConfig := NewPipelineConfig(globals) ith := new(plugins_ts.InputTestHelper) ith.Msg = pipeline_ts.GetTestMessage() ith.Pack = NewPipelinePack(pConfig.InputRecycleChan()) // Specify localhost, but we're not really going to use the network. ith.AddrStr = "localhost:55565" ith.ResolvedAddrStr = "127.0.0.1:55565" // Set up mock helper, runner, and pack supply channel. ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) ith.MockDeliverer = pipelinemock.NewMockDeliverer(ctrl) ith.MockSplitterRunner = pipelinemock.NewMockSplitterRunner(ctrl) ith.PackSupply = make(chan *PipelinePack, 1) c.Specify("A LogstreamerInput", func() { lsInput := &LogstreamerInput{pConfig: pConfig} lsiConfig := lsInput.ConfigStruct().(*LogstreamerInputConfig) lsiConfig.LogDirectory = dirPath lsiConfig.FileMatch = `file.log(\.?)(?P<Seq>\d+)?` lsiConfig.Differentiator = []string{"logfile"} lsiConfig.Priority = []string{"^Seq"} c.Specify("w/ no translation map", func() { err := lsInput.Init(lsiConfig) c.Expect(err, gs.IsNil) c.Expect(len(lsInput.plugins), gs.Equals, 1) // Create pool of packs. numLines := 5 // # of lines in the log file we're parsing. packs := make([]*PipelinePack, numLines) ith.PackSupply = make(chan *PipelinePack, numLines) for i := 0; i < numLines; i++ { packs[i] = NewPipelinePack(ith.PackSupply) ith.PackSupply <- packs[i] } c.Specify("reads a log file", func() { // Expect InputRunner calls to get InChan and inject outgoing msgs. ith.MockInputRunner.EXPECT().LogError(gomock.Any()).AnyTimes() ith.MockInputRunner.EXPECT().LogMessage(gomock.Any()).AnyTimes() ith.MockInputRunner.EXPECT().NewDeliverer("1").Return(ith.MockDeliverer) ith.MockInputRunner.EXPECT().NewSplitterRunner("1").Return( ith.MockSplitterRunner) ith.MockSplitterRunner.EXPECT().UseMsgBytes().Return(false) ith.MockSplitterRunner.EXPECT().IncompleteFinal().Return(false) ith.MockSplitterRunner.EXPECT().SetPackDecorator(gomock.Any()) getRecCall := ith.MockSplitterRunner.EXPECT().GetRecordFromStream( gomock.Any()).Times(numLines) line := "boo hoo foo foo" getRecCall.Return(len(line), []byte(line), nil) getRecCall = ith.MockSplitterRunner.EXPECT().GetRecordFromStream(gomock.Any()) getRecCall.Return(0, make([]byte, 0), io.EOF) deliverChan := make(chan []byte, 1) deliverCall := ith.MockSplitterRunner.EXPECT().DeliverRecord(gomock.Any(), ith.MockDeliverer).Times(numLines) deliverCall.Do(func(record []byte, del Deliverer) { deliverChan <- record }) ith.MockDeliverer.EXPECT().Done() runOutChan := make(chan error, 1) go func() { err = lsInput.Run(ith.MockInputRunner, ith.MockHelper) runOutChan <- err }() dur, _ := time.ParseDuration("5s") timeout := time.After(dur) timed := false for x := 0; x < numLines; x++ { select { case record := <-deliverChan: c.Expect(string(record), gs.Equals, line) case <-timeout: timed = true x += numLines } // Free up the scheduler while we wait for the log file lines // to be processed. runtime.Gosched() } lsInput.Stop() c.Expect(timed, gs.Equals, false) c.Expect(<-runOutChan, gs.Equals, nil) }) }) c.Specify("with a translation map", func() { lsiConfig.Translation = make(ls.SubmatchTranslationMap) lsiConfig.Translation["Seq"] = make(ls.MatchTranslationMap) c.Specify("allows len 1 translation map for 'missing'", func() { lsiConfig.Translation["Seq"]["missing"] = 9999 err := lsInput.Init(lsiConfig) c.Expect(err, gs.IsNil) }) c.Specify("doesn't allow len 1 map for other keys", func() { lsiConfig.Translation["Seq"]["missin"] = 9999 err := lsInput.Init(lsiConfig) c.Expect(err, gs.Not(gs.IsNil)) c.Expect(err.Error(), gs.Equals, "A translation map with one entry ('Seq') must be specifying a "+ "'missing' key.") }) }) }) }
func ProcessInputSpec(c gs.Context) { t := &pipeline_ts.SimpleT{} ctrl := gomock.NewController(t) defer ctrl.Finish() pConfig := NewPipelineConfig(nil) ith := new(plugins_ts.InputTestHelper) ith.Msg = pipeline_ts.GetTestMessage() ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) ith.MockDeliverer = pipelinemock.NewMockDeliverer(ctrl) ith.MockSplitterRunner = pipelinemock.NewMockSplitterRunner(ctrl) ith.Pack = NewPipelinePack(pConfig.InputRecycleChan()) c.Specify("A ProcessInput", func() { pInput := ProcessInput{} config := pInput.ConfigStruct().(*ProcessInputConfig) config.Command = make(map[string]cmdConfig) ith.MockHelper.EXPECT().Hostname().Return(pConfig.Hostname()) tickChan := make(chan time.Time) ith.MockInputRunner.EXPECT().Ticker().Return(tickChan) errChan := make(chan error) ith.MockSplitterRunner.EXPECT().UseMsgBytes().Return(false) decChan := make(chan func(*PipelinePack), 1) setDecCall := ith.MockSplitterRunner.EXPECT().SetPackDecorator(gomock.Any()) setDecCall.Do(func(dec func(*PipelinePack)) { decChan <- dec }) bytesChan := make(chan []byte, 1) splitCall := ith.MockSplitterRunner.EXPECT().SplitStream(gomock.Any(), ith.MockDeliverer).Return(nil) splitCall.Do(func(r io.Reader, del Deliverer) { bytes, err := ioutil.ReadAll(r) c.Assume(err, gs.IsNil) bytesChan <- bytes }) ith.MockDeliverer.EXPECT().Done() c.Specify("using stdout", func() { ith.MockInputRunner.EXPECT().NewDeliverer("stdout").Return(ith.MockDeliverer) ith.MockInputRunner.EXPECT().NewSplitterRunner("stdout").Return( ith.MockSplitterRunner) c.Specify("reads a message from ProcessInput", func() { pInput.SetName("SimpleTest") // Note that no working directory is explicitly specified. config.Command["0"] = cmdConfig{ Bin: PROCESSINPUT_TEST1_CMD, Args: PROCESSINPUT_TEST1_CMD_ARGS, } err := pInput.Init(config) c.Assume(err, gs.IsNil) go func() { errChan <- pInput.Run(ith.MockInputRunner, ith.MockHelper) }() tickChan <- time.Now() actual := <-bytesChan c.Expect(string(actual), gs.Equals, PROCESSINPUT_TEST1_OUTPUT+"\n") dec := <-decChan dec(ith.Pack) fPInputName := ith.Pack.Message.FindFirstField("ProcessInputName") c.Expect(fPInputName.ValueString[0], gs.Equals, "SimpleTest.stdout") pInput.Stop() err = <-errChan c.Expect(err, gs.IsNil) }) c.Specify("can pipe multiple commands together", func() { pInput.SetName("PipedCmd") // Note that no working directory is explicitly specified. config.Command["0"] = cmdConfig{ Bin: PROCESSINPUT_PIPE_CMD1, Args: PROCESSINPUT_PIPE_CMD1_ARGS, } config.Command["1"] = cmdConfig{ Bin: PROCESSINPUT_PIPE_CMD2, Args: PROCESSINPUT_PIPE_CMD2_ARGS, } err := pInput.Init(config) c.Assume(err, gs.IsNil) go func() { errChan <- pInput.Run(ith.MockInputRunner, ith.MockHelper) }() tickChan <- time.Now() actual := <-bytesChan c.Expect(string(actual), gs.Equals, PROCESSINPUT_PIPE_OUTPUT+"\n") dec := <-decChan dec(ith.Pack) fPInputName := ith.Pack.Message.FindFirstField("ProcessInputName") c.Expect(fPInputName.ValueString[0], gs.Equals, "PipedCmd.stdout") pInput.Stop() err = <-errChan c.Expect(err, gs.IsNil) }) }) c.Specify("using stderr", func() { ith.MockInputRunner.EXPECT().NewDeliverer("stderr").Return(ith.MockDeliverer) ith.MockInputRunner.EXPECT().NewSplitterRunner("stderr").Return( ith.MockSplitterRunner) c.Specify("handles bad arguments", func() { pInput.SetName("BadArgs") config.ParseStdout = false config.ParseStderr = true // Note that no working directory is explicitly specified. config.Command["0"] = cmdConfig{Bin: STDERR_CMD, Args: STDERR_CMD_ARGS} err := pInput.Init(config) c.Assume(err, gs.IsNil) expectedErr := fmt.Errorf( "BadArgs CommandChain::Wait() error: [Subcommand returned an error: [exit status 1]]") ith.MockInputRunner.EXPECT().LogError(expectedErr) go func() { errChan <- pInput.Run(ith.MockInputRunner, ith.MockHelper) }() tickChan <- time.Now() // Error message differs by platform, but we at least wait // until we get it. <-bytesChan pInput.Stop() err = <-errChan c.Expect(err, gs.IsNil) }) }) }) }
func ProcessInputSpec(c gs.Context) { t := &pipeline_ts.SimpleT{} ctrl := gomock.NewController(t) defer ctrl.Finish() config := NewPipelineConfig(nil) ith := new(plugins_ts.InputTestHelper) ith.Msg = pipeline_ts.GetTestMessage() ith.Pack = NewPipelinePack(config.InputRecycleChan()) // Specify localhost, but we're not really going to use the network ith.AddrStr = "localhost:55565" ith.ResolvedAddrStr = "127.0.0.1:55565" // set up mock helper, decoder set, and packSupply channel ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) ith.Decoder = pipelinemock.NewMockDecoderRunner(ctrl) ith.PackSupply = make(chan *PipelinePack, 1) ith.DecodeChan = make(chan *PipelinePack) c.Specify("A ProcessInput", func() { pInput := ProcessInput{} ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply).AnyTimes() ith.MockInputRunner.EXPECT().Name().Return("logger").AnyTimes() enccall := ith.MockHelper.EXPECT().DecoderRunner("RegexpDecoder", "logger-RegexpDecoder").AnyTimes() enccall.Return(ith.Decoder, true) mockDecoderRunner := ith.Decoder.(*pipelinemock.MockDecoderRunner) mockDecoderRunner.EXPECT().InChan().Return(ith.DecodeChan).AnyTimes() config := pInput.ConfigStruct().(*ProcessInputConfig) config.Command = make(map[string]cmdConfig) pConfig := NewPipelineConfig(nil) ith.MockHelper.EXPECT().PipelineConfig().Return(pConfig) tickChan := make(chan time.Time) ith.MockInputRunner.EXPECT().Ticker().Return(tickChan) c.Specify("reads a message from ProcessInput", func() { pInput.SetName("SimpleTest") config.Decoder = "RegexpDecoder" config.ParserType = "token" config.Delimiter = "|" // Note that no working directory is explicitly specified config.Command["0"] = cmdConfig{Bin: PROCESSINPUT_TEST1_CMD, Args: PROCESSINPUT_TEST1_CMD_ARGS} err := pInput.Init(config) c.Assume(err, gs.IsNil) go func() { pInput.Run(ith.MockInputRunner, ith.MockHelper) }() tickChan <- time.Now() expected_payloads := PROCESSINPUT_TEST1_OUTPUT actual_payloads := []string{} for x := 0; x < 4; x++ { ith.PackSupply <- ith.Pack packRef := <-ith.DecodeChan c.Expect(ith.Pack, gs.Equals, packRef) actual_payloads = append(actual_payloads, *packRef.Message.Payload) fPInputName := *packRef.Message.FindFirstField("ProcessInputName") c.Expect(fPInputName.ValueString[0], gs.Equals, "SimpleTest.stdout") // Free up the scheduler runtime.Gosched() } for x := 0; x < 4; x++ { c.Expect(expected_payloads[x], gs.Equals, actual_payloads[x]) } pInput.Stop() }) c.Specify("handles bad arguments", func() { pInput.SetName("BadArgs") config.ParseStdout = false config.ParseStderr = true config.Decoder = "RegexpDecoder" config.ParserType = "token" config.Delimiter = "|" // Note that no working directory is explicitly specified config.Command["0"] = cmdConfig{Bin: STDERR_CMD, Args: STDERR_CMD_ARGS} err := pInput.Init(config) c.Assume(err, gs.IsNil) expected_err := fmt.Errorf("BadArgs CommandChain::Wait() error: [Subcommand returned an error: [exit status 1]]") ith.MockInputRunner.EXPECT().LogError(expected_err) go func() { pInput.Run(ith.MockInputRunner, ith.MockHelper) }() tickChan <- time.Now() ith.PackSupply <- ith.Pack <-ith.DecodeChan runtime.Gosched() pInput.Stop() }) c.Specify("can pipe multiple commands together", func() { pInput.SetName("PipedCmd") config.Decoder = "RegexpDecoder" config.ParserType = "token" // Overload the delimiter config.Delimiter = " " // Note that no working directory is explicitly specified config.Command["0"] = cmdConfig{Bin: PROCESSINPUT_PIPE_CMD1, Args: PROCESSINPUT_PIPE_CMD1_ARGS} config.Command["1"] = cmdConfig{Bin: PROCESSINPUT_PIPE_CMD2, Args: PROCESSINPUT_PIPE_CMD2_ARGS} err := pInput.Init(config) c.Assume(err, gs.IsNil) go func() { pInput.Run(ith.MockInputRunner, ith.MockHelper) }() tickChan <- time.Now() expected_payloads := PROCESSINPUT_PIPE_OUTPUT actual_payloads := []string{} for x := 0; x < len(PROCESSINPUT_PIPE_OUTPUT); x++ { ith.PackSupply <- ith.Pack packRef := <-ith.DecodeChan c.Expect(ith.Pack, gs.Equals, packRef) actual_payloads = append(actual_payloads, *packRef.Message.Payload) fPInputName := *packRef.Message.FindFirstField("ProcessInputName") c.Expect(fPInputName.ValueString[0], gs.Equals, "PipedCmd.stdout") // Free up the scheduler runtime.Gosched() } for x := 0; x < len(PROCESSINPUT_PIPE_OUTPUT); x++ { c.Expect(fmt.Sprintf("[%d] [%s] [%x]", len(actual_payloads[x]), actual_payloads[x], actual_payloads[x]), gs.Equals, fmt.Sprintf("[%d] [%s] [%x]", len(expected_payloads[x]), expected_payloads[x], expected_payloads[x])) } pInput.Stop() }) }) }
func HttpListenInputSpec(c gs.Context) { t := &pipeline_ts.SimpleT{} ctrl := gomock.NewController(t) defer ctrl.Finish() pConfig := NewPipelineConfig(nil) ith := new(plugins_ts.InputTestHelper) ith.Pack = NewPipelinePack(pConfig.InputRecycleChan()) httpListenInput := HttpListenInput{} ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) ith.MockSplitterRunner = pipelinemock.NewMockSplitterRunner(ctrl) splitter := &TokenSplitter{} // Not actually used. errChan := make(chan error, 1) startInput := func() { go func() { err := httpListenInput.Run(ith.MockInputRunner, ith.MockHelper) errChan <- err }() } config := httpListenInput.ConfigStruct().(*HttpListenInputConfig) config.Address = "127.0.0.1:58325" c.Specify("A HttpListenInput", func() { startedChan := make(chan bool, 1) defer close(startedChan) ts := httptest.NewUnstartedServer(nil) httpListenInput.starterFunc = func(hli *HttpListenInput) error { ts.Start() startedChan <- true return nil } // These EXPECTs imply that every spec below will send exactly one // HTTP request to the input. ith.MockInputRunner.EXPECT().NewSplitterRunner(gomock.Any()).Return( ith.MockSplitterRunner) ith.MockSplitterRunner.EXPECT().UseMsgBytes().Return(false) ith.MockSplitterRunner.EXPECT().Splitter().Return(splitter) decChan := make(chan func(*PipelinePack), 1) feedDecorator := func(decorator func(*PipelinePack)) { decChan <- decorator } setDecCall := ith.MockSplitterRunner.EXPECT().SetPackDecorator(gomock.Any()) setDecCall.Do(feedDecorator) streamChan := make(chan io.Reader, 1) feedStream := func(r io.Reader) { streamChan <- r } getRecCall := ith.MockSplitterRunner.EXPECT().GetRecordFromStream( gomock.Any()).Do(feedStream) bytesChan := make(chan []byte, 1) deliver := func(msgBytes []byte, del Deliverer) { bytesChan <- msgBytes } ith.MockSplitterRunner.EXPECT().IncompleteFinal().Return(false).AnyTimes() c.Specify("Adds query parameters to the message pack as fields", func() { err := httpListenInput.Init(config) c.Assume(err, gs.IsNil) ts.Config = httpListenInput.server getRecCall.Return(0, make([]byte, 0), io.EOF) startInput() <-startedChan resp, err := http.Get(ts.URL + "/?test=Hello%20World") resp.Body.Close() c.Assume(err, gs.IsNil) c.Assume(resp.StatusCode, gs.Equals, 200) packDec := <-decChan packDec(ith.Pack) fieldValue, ok := ith.Pack.Message.GetFieldValue("test") c.Assume(ok, gs.IsTrue) c.Expect(fieldValue, gs.Equals, "Hello World") }) c.Specify("Add custom headers", func() { config.Headers = http.Header{ "One": []string{"two", "three"}, "Four": []string{"five", "six", "seven"}, } err := httpListenInput.Init(config) c.Assume(err, gs.IsNil) ts.Config = httpListenInput.server getRecCall.Return(0, make([]byte, 0), io.EOF) startInput() <-startedChan resp, err := http.Get(ts.URL) c.Assume(err, gs.IsNil) resp.Body.Close() c.Assume(resp.StatusCode, gs.Equals, 200) packDec := <-decChan packDec(ith.Pack) // Verify headers are there eq := reflect.DeepEqual(resp.Header["One"], config.Headers["One"]) c.Expect(eq, gs.IsTrue) eq = reflect.DeepEqual(resp.Header["Four"], config.Headers["Four"]) c.Expect(eq, gs.IsTrue) }) c.Specify("Request body is sent as record", func() { err := httpListenInput.Init(config) c.Assume(err, gs.IsNil) ts.Config = httpListenInput.server body := "1+2" getRecCall.Return(0, []byte(body), io.EOF) startInput() <-startedChan deliverCall := ith.MockSplitterRunner.EXPECT().DeliverRecord(gomock.Any(), nil) deliverCall.Do(deliver) resp, err := http.Post(ts.URL, "text/plain", strings.NewReader(body)) c.Assume(err, gs.IsNil) resp.Body.Close() c.Assume(resp.StatusCode, gs.Equals, 200) msgBytes := <-bytesChan c.Expect(string(msgBytes), gs.Equals, "1+2") }) c.Specify("Add request headers as fields", func() { config.RequestHeaders = []string{ "X-REQUEST-ID", } err := httpListenInput.Init(config) c.Assume(err, gs.IsNil) ts.Config = httpListenInput.server getRecCall.Return(0, make([]byte, 0), io.EOF) startInput() <-startedChan client := &http.Client{} req, err := http.NewRequest("GET", ts.URL, nil) req.Header.Add("X-REQUEST-ID", "12345") resp, err := client.Do(req) c.Assume(err, gs.IsNil) resp.Body.Close() c.Assume(resp.StatusCode, gs.Equals, 200) packDec := <-decChan packDec(ith.Pack) fieldValue, ok := ith.Pack.Message.GetFieldValue("X-REQUEST-ID") c.Assume(ok, gs.IsTrue) c.Expect(fieldValue, gs.Equals, "12345") }) c.Specify("Add other request properties as fields", func() { err := httpListenInput.Init(config) c.Assume(err, gs.IsNil) ts.Config = httpListenInput.server getRecCall.Return(0, make([]byte, 0), io.EOF) startInput() <-startedChan client := &http.Client{} req, err := http.NewRequest("GET", ts.URL+"/foo/bar?baz=2", nil) c.Assume(err, gs.IsNil) req.Host = "incoming.example.com:8080" resp, err := client.Do(req) resp.Body.Close() c.Assume(err, gs.IsNil) c.Assume(resp.StatusCode, gs.Equals, 200) packDec := <-decChan packDec(ith.Pack) fieldValue, ok := ith.Pack.Message.GetFieldValue("Host") c.Assume(ok, gs.IsTrue) // Host should not include port number. c.Expect(fieldValue, gs.Equals, "incoming.example.com") fieldValue, ok = ith.Pack.Message.GetFieldValue("Path") c.Assume(ok, gs.IsTrue) c.Expect(fieldValue, gs.Equals, "/foo/bar") hostname, err := os.Hostname() c.Assume(err, gs.IsNil) c.Expect(*ith.Pack.Message.Hostname, gs.Equals, hostname) c.Expect(*ith.Pack.Message.EnvVersion, gs.Equals, "1") fieldValue, ok = ith.Pack.Message.GetFieldValue("RemoteAddr") c.Assume(ok, gs.IsTrue) c.Expect(fieldValue != nil, gs.IsTrue) fieldValueStr, ok := fieldValue.(string) c.Assume(ok, gs.IsTrue) c.Expect(len(fieldValueStr) > 0, gs.IsTrue) // Per the `Request` docs, this should be an IP address: // http://golang.org/pkg/net/http/#Request ip := net.ParseIP(fieldValueStr) c.Expect(ip != nil, gs.IsTrue) }) ts.Close() httpListenInput.Stop() err := <-errChan c.Expect(err, gs.IsNil) }) }
func NagiosOutputSpec(c gs.Context) { t := new(pipeline_ts.SimpleT) ctrl := gomock.NewController(t) defer ctrl.Finish() c.Specify("A NagiosOutput", func() { output := new(NagiosOutput) config := output.ConfigStruct().(*NagiosOutputConfig) config.Url = "http://localhost:55580/foo/bar" mockOutputRunner := pipelinemock.NewMockOutputRunner(ctrl) mockHelper := pipelinemock.NewMockPluginHelper(ctrl) inChan := make(chan *pipeline.PipelinePack) recycleChan := make(chan *pipeline.PipelinePack, 1) pack := pipeline.NewPipelinePack(recycleChan) msg := pipeline_ts.GetTestMessage() pack.Message = msg var req *http.Request var outputWg, reqWg sync.WaitGroup run := func() { mockOutputRunner.EXPECT().InChan().Return(inChan) mockOutputRunner.EXPECT().UpdateCursor("").AnyTimes() output.Run(mockOutputRunner, mockHelper) outputWg.Done() } const payload = "something" c.Specify("using HTTP transport", func() { // Spin up an HTTP listener. listener, err := net.Listen("tcp", "127.0.0.1:55580") c.Assume(err, gs.IsNil) mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Connection", "close") r.ParseForm() req = r listener.Close() reqWg.Done() }) go http.Serve(listener, mux) c.Specify("sends a valid HTTP POST", func() { err = output.Init(config) c.Assume(err, gs.IsNil) outputWg.Add(1) go run() msg.SetPayload("OK:" + payload) reqWg.Add(1) inChan <- pack close(inChan) outputWg.Wait() reqWg.Wait() c.Expect(req.FormValue("plugin_output"), gs.Equals, payload) c.Expect(req.FormValue("plugin_state"), gs.Equals, "0") }) c.Specify("correctly maps alternate state", func() { err = output.Init(config) c.Assume(err, gs.IsNil) outputWg.Add(1) go run() msg.SetPayload("CRITICAL:" + payload) reqWg.Add(1) inChan <- pack close(inChan) outputWg.Wait() reqWg.Wait() c.Expect(req.FormValue("plugin_output"), gs.Equals, payload) c.Expect(req.FormValue("plugin_state"), gs.Equals, "2") }) }) if runtime.GOOS != "windows" { outPath := filepath.Join(os.TempDir(), "heka-nagios-test-output.txt") echoFile := fmt.Sprintf(echoFileTmpl, outPath) c.Specify("using a send_ncsa binary", func() { binPath := pipeline_ts.WriteStringToTmpFile(echoFile) os.Chmod(binPath, 0700) defer func() { os.Remove(binPath) os.Remove(outPath) }() config.SendNscaBin = binPath config.SendNscaArgs = []string{"arg1", "arg2"} err := output.Init(config) c.Assume(err, gs.IsNil) outputWg.Add(1) go run() c.Specify("sends args and the right data through", func() { msg.SetPayload("OK:" + payload) inChan <- pack close(inChan) outputWg.Wait() outFile, err := os.Open(outPath) c.Expect(err, gs.IsNil) reader := bufio.NewReader(outFile) line, _, err := reader.ReadLine() c.Expect(err, gs.IsNil) c.Expect(string(line), gs.Equals, strings.Join(config.SendNscaArgs, " ")) line, _, err = reader.ReadLine() c.Expect(err, gs.IsNil) c.Expect(string(line), gs.Equals, "my.host.name GoSpec 0 "+payload) }) c.Specify("correctly maps alternate state", func() { msg.SetPayload("WARNING:" + payload) inChan <- pack close(inChan) outputWg.Wait() outFile, err := os.Open(outPath) c.Expect(err, gs.IsNil) reader := bufio.NewReader(outFile) line, _, err := reader.ReadLine() c.Expect(err, gs.IsNil) c.Expect(string(line), gs.Equals, strings.Join(config.SendNscaArgs, " ")) line, _, err = reader.ReadLine() c.Expect(err, gs.IsNil) c.Expect(string(line), gs.Equals, "my.host.name GoSpec 1 "+payload) }) }) } }) }
func InputSpec(c gs.Context) { t := new(pipeline_ts.SimpleT) ctrl := gomock.NewController(t) var wg sync.WaitGroup errChan := make(chan error, 1) tickChan := make(chan time.Time) defer func() { close(tickChan) close(errChan) ctrl.Finish() }() pConfig := NewPipelineConfig(nil) c.Specify("A SandboxInput", func() { input := new(SandboxInput) input.SetPipelineConfig(pConfig) ith := new(plugins_ts.InputTestHelper) ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) ith.PackSupply = make(chan *PipelinePack, 1) ith.Pack = NewPipelinePack(ith.PackSupply) ith.PackSupply <- ith.Pack startInput := func() { wg.Add(1) go func() { errChan <- input.Run(ith.MockInputRunner, ith.MockHelper) wg.Done() }() } c.Specify("test a polling input", func() { tickChan := time.Tick(10 * time.Millisecond) ith.MockInputRunner.EXPECT().Ticker().Return(tickChan) ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply).Times(2) ith.MockInputRunner.EXPECT().LogError(fmt.Errorf("failure message")) var cnt int ith.MockInputRunner.EXPECT().Inject(gomock.Any()).Do( func(pack *PipelinePack) { switch cnt { case 0: c.Expect(pack.Message.GetPayload(), gs.Equals, "line 1") case 1: c.Expect(pack.Message.GetPayload(), gs.Equals, "line 3") input.Stop() } cnt++ ith.PackSupply <- pack }).Times(2) config := input.ConfigStruct().(*sandbox.SandboxConfig) config.ScriptFilename = "../lua/testsupport/input.lua" err := input.Init(config) c.Assume(err, gs.IsNil) startInput() wg.Wait() c.Expect(<-errChan, gs.IsNil) c.Expect(input.processMessageCount, gs.Equals, int64(2)) c.Expect(input.processMessageFailures, gs.Equals, int64(1)) c.Expect(input.processMessageBytes, gs.Equals, int64(72)) }) c.Specify("run once input", func() { var tickChan <-chan time.Time ith.MockInputRunner.EXPECT().Ticker().Return(tickChan) ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply).Times(1) ith.MockInputRunner.EXPECT().LogMessage("single run completed") ith.MockInputRunner.EXPECT().Inject(gomock.Any()).Do( func(pack *PipelinePack) { c.Expect(pack.Message.GetPayload(), gs.Equals, "line 1") ith.PackSupply <- pack }).Times(1) config := input.ConfigStruct().(*sandbox.SandboxConfig) config.ScriptFilename = "../lua/testsupport/input.lua" err := input.Init(config) c.Assume(err, gs.IsNil) startInput() wg.Wait() c.Expect(<-errChan, gs.IsNil) c.Expect(input.processMessageCount, gs.Equals, int64(1)) c.Expect(input.processMessageBytes, gs.Equals, int64(36)) }) c.Specify("exit with error", func() { tickChan := make(chan time.Time) defer close(tickChan) ith.MockInputRunner.EXPECT().Ticker().Return(tickChan) ith.MockInputRunner.EXPECT().LogError(fmt.Errorf("process_message() ../lua/testsupport/input_error.lua:2: boom")) config := input.ConfigStruct().(*sandbox.SandboxConfig) config.ScriptFilename = "../lua/testsupport/input_error.lua" err := input.Init(config) c.Assume(err, gs.IsNil) startInput() wg.Wait() c.Expect(<-errChan, gs.IsNil) c.Expect(input.processMessageCount, gs.Equals, int64(0)) c.Expect(input.processMessageBytes, gs.Equals, int64(0)) }) }) }
func TestReceiveProtobufMessage(t *testing.T) { broker := sarama.NewMockBroker(t, 2) ctrl := gomock.NewController(t) tmpDir, tmpErr := ioutil.TempDir("", "kafkainput-tests") if tmpErr != nil { t.Errorf("Unable to create a temporary directory: %s", tmpErr) } defer func() { if err := os.RemoveAll(tmpDir); err != nil { t.Errorf("Cleanup failed: %s", err) } ctrl.Finish() }() topic := "test" mockFetchResponse := sarama.NewMockFetchResponse(t, 1) mockFetchResponse.SetMessage(topic, 0, 0, sarama.ByteEncoder([]byte{0x41, 0x42})) broker.SetHandlerByMap(map[string]sarama.MockResponse{ "MetadataRequest": sarama.NewMockMetadataResponse(t). SetBroker(broker.Addr(), broker.BrokerID()). SetLeader(topic, 0, broker.BrokerID()), "OffsetRequest": sarama.NewMockOffsetResponse(t). SetOffset(topic, 0, sarama.OffsetOldest, 0). SetOffset(topic, 0, sarama.OffsetNewest, 2), "FetchRequest": mockFetchResponse, }) pConfig := NewPipelineConfig(nil) pConfig.Globals.BaseDir = tmpDir ki := new(KafkaInput) ki.SetName(topic) ki.SetPipelineConfig(pConfig) config := ki.ConfigStruct().(*KafkaInputConfig) config.Addrs = append(config.Addrs, broker.Addr()) config.Topic = topic ith := new(plugins_ts.InputTestHelper) ith.Pack = NewPipelinePack(pConfig.InputRecycleChan()) ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) ith.MockSplitterRunner = pipelinemock.NewMockSplitterRunner(ctrl) err := ki.Init(config) if err != nil { t.Fatalf("%s", err) } ith.MockInputRunner.EXPECT().NewSplitterRunner("").Return(ith.MockSplitterRunner) ith.MockSplitterRunner.EXPECT().UseMsgBytes().Return(true) ith.MockSplitterRunner.EXPECT().Done() bytesChan := make(chan []byte, 1) splitCall := ith.MockSplitterRunner.EXPECT().SplitBytes(gomock.Any(), nil) splitCall.Do(func(recd []byte, del Deliverer) { bytesChan <- recd }) errChan := make(chan error) go func() { errChan <- ki.Run(ith.MockInputRunner, ith.MockHelper) }() recd := <-bytesChan if string(recd) != "AB" { t.Errorf("Invalid MsgBytes Expected: AB received: %s", string(recd)) } // There is a hang on the consumer close with the mock broker // closing the brokers before the consumer works around the issue // and is good enough for this test. broker.Close() ki.Stop() err = <-errChan if err != nil { t.Fatal(err) } }
func LogfileInputSpec0(c gs.Context) { t := &pipeline_ts.SimpleT{} ctrl := gomock.NewController(t) defer ctrl.Finish() config := NewPipelineConfig(nil) ith := new(plugins_ts.InputTestHelper) ith.Msg = pipeline_ts.GetTestMessage() ith.Pack = NewPipelinePack(config.InputRecycleChan()) // Specify localhost, but we're not really going to use the network ith.AddrStr = "localhost:55565" ith.ResolvedAddrStr = "127.0.0.1:55565" // set up mock helper, decoder set, and packSupply channel ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) ith.Decoder = pipelinemock.NewMockDecoderRunner(ctrl) ith.PackSupply = make(chan *PipelinePack, 1) ith.DecodeChan = make(chan *PipelinePack) c.Specify("A LogFileInput", func() { tmpDir, tmpErr := ioutil.TempDir("", "hekad-tests-") c.Expect(tmpErr, gs.Equals, nil) origBaseDir := Globals().BaseDir Globals().BaseDir = tmpDir defer func() { Globals().BaseDir = origBaseDir tmpErr = os.RemoveAll(tmpDir) c.Expect(tmpErr, gs.IsNil) }() lfInput := new(LogfileInput) lfiConfig := lfInput.ConfigStruct().(*LogfileInputConfig) lfiConfig.SeekJournalName = "test-seekjournal" lfiConfig.LogFile = "../testsupport/test-zeus.log" lfiConfig.Logger = "zeus" lfiConfig.UseSeekJournal = true lfiConfig.Decoder = "decoder-name" lfiConfig.DiscoverInterval = 1 lfiConfig.StatInterval = 1 err := lfInput.Init(lfiConfig) c.Expect(err, gs.IsNil) mockDecoderRunner := pipelinemock.NewMockDecoderRunner(ctrl) // Create pool of packs. numLines := 95 // # of lines in the log file we're parsing. packs := make([]*PipelinePack, numLines) ith.PackSupply = make(chan *PipelinePack, numLines) for i := 0; i < numLines; i++ { packs[i] = NewPipelinePack(ith.PackSupply) ith.PackSupply <- packs[i] } c.Specify("reads a log file", func() { // Expect InputRunner calls to get InChan and inject outgoing msgs ith.MockInputRunner.EXPECT().LogError(gomock.Any()).AnyTimes() ith.MockInputRunner.EXPECT().LogMessage(gomock.Any()).AnyTimes() ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply).Times(numLines) // Expect calls to get decoder and decode each message. Since the // decoding is a no-op, the message payload will be the log file // line, unchanged. ith.MockInputRunner.EXPECT().Name().Return("LogfileInput") pbcall := ith.MockHelper.EXPECT().DecoderRunner(lfiConfig.Decoder, "LogfileInput-"+lfiConfig.Decoder) pbcall.Return(mockDecoderRunner, true) decodeCall := mockDecoderRunner.EXPECT().InChan().Times(numLines) decodeCall.Return(ith.DecodeChan) go func() { err = lfInput.Run(ith.MockInputRunner, ith.MockHelper) c.Expect(err, gs.IsNil) }() for x := 0; x < numLines; x++ { _ = <-ith.DecodeChan // Free up the scheduler while we wait for the log file lines // to be processed. runtime.Gosched() } lfInput.Stop() fileBytes, err := ioutil.ReadFile(lfiConfig.LogFile) c.Expect(err, gs.IsNil) fileStr := string(fileBytes) lines := strings.Split(fileStr, "\n") for i, line := range lines { if line == "" { continue } c.Expect(packs[i].Message.GetPayload(), gs.Equals, line+"\n") c.Expect(packs[i].Message.GetLogger(), gs.Equals, "zeus") } // Wait for the file update to hit the disk; better suggestions are welcome runtime.Gosched() time.Sleep(time.Millisecond * 250) journalData := []byte(`{"last_hash":"f0b60af7f2cb35c3724151422e2f999af6e21fc0","last_len":300,"last_start":28650,"seek":28950}`) journalFile, err := ioutil.ReadFile(filepath.Join(tmpDir, "seekjournals", lfiConfig.SeekJournalName)) c.Expect(err, gs.IsNil) c.Expect(bytes.Compare(journalData, journalFile), gs.Equals, 0) }) c.Specify("uses the filename as the default logger name", func() { lfInput := new(LogfileInput) lfiConfig := lfInput.ConfigStruct().(*LogfileInputConfig) lfiConfig.LogFile = "../testsupport/test-zeus.log" lfiConfig.DiscoverInterval = 1 lfiConfig.StatInterval = 1 err := lfInput.Init(lfiConfig) c.Expect(err, gs.Equals, nil) c.Expect(lfInput.Monitor.logger_ident, gs.Equals, lfiConfig.LogFile) }) }) c.Specify("A Regex LogFileInput", func() { tmpDir, tmpErr := ioutil.TempDir("", "hekad-tests-") c.Expect(tmpErr, gs.Equals, nil) origBaseDir := Globals().BaseDir Globals().BaseDir = tmpDir defer func() { Globals().BaseDir = origBaseDir tmpErr = os.RemoveAll(tmpDir) c.Expect(tmpErr, gs.Equals, nil) }() lfInput := new(LogfileInput) lfiConfig := lfInput.ConfigStruct().(*LogfileInputConfig) lfiConfig.SeekJournalName = "regex-seekjournal" lfiConfig.LogFile = "../testsupport/test-zeus.log" lfiConfig.Logger = "zeus" lfiConfig.ParserType = "regexp" lfiConfig.UseSeekJournal = true lfiConfig.Decoder = "decoder-name" lfiConfig.DiscoverInterval = 1 lfiConfig.StatInterval = 1 mockDecoderRunner := pipelinemock.NewMockDecoderRunner(ctrl) // Create pool of packs. numLines := 95 // # of lines in the log file we're parsing. packs := make([]*PipelinePack, numLines) ith.PackSupply = make(chan *PipelinePack, numLines) for i := 0; i < numLines; i++ { packs[i] = NewPipelinePack(ith.PackSupply) ith.PackSupply <- packs[i] } c.Specify("doesn't whack default RegexpParser delimiter", func() { err := lfInput.Init(lfiConfig) c.Expect(err, gs.IsNil) parser := lfInput.Monitor.parser.(*RegexpParser) buf := bytes.NewBuffer([]byte("split\nhere")) n, r, err := parser.Parse(buf) c.Expect(n, gs.Equals, len("split\n")) c.Expect(string(r), gs.Equals, "split") c.Expect(err, gs.IsNil) }) c.Specify("keeps captures in the record text", func() { lfiConfig.Delimiter = "(\n)" err := lfInput.Init(lfiConfig) c.Expect(err, gs.IsNil) parser := lfInput.Monitor.parser.(*RegexpParser) buf := bytes.NewBuffer([]byte("split\nhere")) n, r, err := parser.Parse(buf) c.Expect(n, gs.Equals, len("split\n")) c.Expect(string(r), gs.Equals, "split\n") c.Expect(err, gs.IsNil) }) c.Specify("reads a log file", func() { lfiConfig.Delimiter = "(\n)" err := lfInput.Init(lfiConfig) c.Expect(err, gs.IsNil) // Expect InputRunner calls to get InChan and inject outgoing msgs ith.MockInputRunner.EXPECT().LogError(gomock.Any()).AnyTimes() ith.MockInputRunner.EXPECT().LogMessage(gomock.Any()).AnyTimes() ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply).Times(numLines) // Expect calls to get decoder and decode each message. Since the // decoding is a no-op, the message payload will be the log file // line, unchanged. ith.MockInputRunner.EXPECT().Name().Return("LogfileInput") pbcall := ith.MockHelper.EXPECT().DecoderRunner(lfiConfig.Decoder, "LogfileInput-"+lfiConfig.Decoder) pbcall.Return(mockDecoderRunner, true) decodeCall := mockDecoderRunner.EXPECT().InChan().Times(numLines) decodeCall.Return(ith.DecodeChan) go func() { err = lfInput.Run(ith.MockInputRunner, ith.MockHelper) c.Expect(err, gs.IsNil) }() for x := 0; x < numLines; x++ { _ = <-ith.DecodeChan // Free up the scheduler while we wait for the log file lines // to be processed. runtime.Gosched() } lfInput.Stop() fileBytes, err := ioutil.ReadFile(lfiConfig.LogFile) c.Expect(err, gs.IsNil) fileStr := string(fileBytes) lines := strings.Split(fileStr, "\n") for i, line := range lines { if line == "" { continue } c.Expect(packs[i].Message.GetPayload(), gs.Equals, line+"\n") c.Expect(packs[i].Message.GetLogger(), gs.Equals, "zeus") } // Wait for the file update to hit the disk; better suggestions are welcome runtime.Gosched() time.Sleep(time.Millisecond * 250) journalData := []byte(`{"last_hash":"f0b60af7f2cb35c3724151422e2f999af6e21fc0","last_len":300,"last_start":28650,"seek":28950}`) journalFile, err := ioutil.ReadFile(filepath.Join(tmpDir, "seekjournals", lfiConfig.SeekJournalName)) c.Expect(err, gs.IsNil) c.Expect(bytes.Compare(journalData, journalFile), gs.Equals, 0) }) }) c.Specify("A Regex Multiline LogFileInput", func() { tmpDir, tmpErr := ioutil.TempDir("", "hekad-tests-") c.Expect(tmpErr, gs.Equals, nil) origBaseDir := Globals().BaseDir Globals().BaseDir = tmpDir defer func() { Globals().BaseDir = origBaseDir tmpErr = os.RemoveAll(tmpDir) c.Expect(tmpErr, gs.Equals, nil) }() lfInput := new(LogfileInput) lfiConfig := lfInput.ConfigStruct().(*LogfileInputConfig) lfiConfig.SeekJournalName = "multiline-seekjournal" lfiConfig.LogFile = "../testsupport/multiline.log" lfiConfig.Logger = "multiline" lfiConfig.ParserType = "regexp" lfiConfig.Delimiter = "\n(\\d{4}-\\d{2}-\\d{2})" lfiConfig.DelimiterLocation = "start" lfiConfig.UseSeekJournal = true lfiConfig.Decoder = "decoder-name" lfiConfig.DiscoverInterval = 1 lfiConfig.StatInterval = 1 err := lfInput.Init(lfiConfig) c.Expect(err, gs.IsNil) mockDecoderRunner := pipelinemock.NewMockDecoderRunner(ctrl) // Create pool of packs. numLines := 4 // # of lines in the log file we're parsing. packs := make([]*PipelinePack, numLines) ith.PackSupply = make(chan *PipelinePack, numLines) for i := 0; i < numLines; i++ { packs[i] = NewPipelinePack(ith.PackSupply) ith.PackSupply <- packs[i] } c.Specify("reads a log file", func() { // Expect InputRunner calls to get InChan and inject outgoing msgs ith.MockInputRunner.EXPECT().LogError(gomock.Any()).AnyTimes() ith.MockInputRunner.EXPECT().LogMessage(gomock.Any()).AnyTimes() ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply).Times(numLines) // Expect calls to get decoder and decode each message. Since the // decoding is a no-op, the message payload will be the log file // line, unchanged. ith.MockInputRunner.EXPECT().Name().Return("LogfileInput") pbcall := ith.MockHelper.EXPECT().DecoderRunner(lfiConfig.Decoder, "LogfileInput-"+lfiConfig.Decoder) pbcall.Return(mockDecoderRunner, true) decodeCall := mockDecoderRunner.EXPECT().InChan().Times(numLines) decodeCall.Return(ith.DecodeChan) go func() { err = lfInput.Run(ith.MockInputRunner, ith.MockHelper) c.Expect(err, gs.IsNil) }() for x := 0; x < numLines; x++ { _ = <-ith.DecodeChan // Free up the scheduler while we wait for the log file lines // to be processed. runtime.Gosched() } lfInput.Stop() lines := []string{ "2012-07-13 18:48:01 debug readSocket()", "2012-07-13 18:48:21 info Processing queue id 3496 -> subm id 2817 from site ms", "2012-07-13 18:48:25 debug line0\nline1\nline2", "2012-07-13 18:48:26 debug readSocket()", } for i, line := range lines { c.Expect(packs[i].Message.GetPayload(), gs.Equals, line) c.Expect(packs[i].Message.GetLogger(), gs.Equals, "multiline") } // Wait for the file update to hit the disk; better suggestions are welcome runtime.Gosched() time.Sleep(time.Millisecond * 250) journalData := []byte(`{"last_hash":"39e4c3e6e9c88a794b3e7c91c155682c34cf1a4a","last_len":41,"last_start":172,"seek":214}`) journalFile, err := ioutil.ReadFile(filepath.Join(tmpDir, "seekjournals", lfiConfig.SeekJournalName)) c.Expect(err, gs.IsNil) c.Expect(bytes.Compare(journalData, journalFile), gs.Equals, 0) }) }) c.Specify("A message.proto LogFileInput", func() { tmpDir, tmpErr := ioutil.TempDir("", "hekad-tests-") c.Expect(tmpErr, gs.Equals, nil) origBaseDir := Globals().BaseDir Globals().BaseDir = tmpDir defer func() { Globals().BaseDir = origBaseDir tmpErr = os.RemoveAll(tmpDir) c.Expect(tmpErr, gs.Equals, nil) }() lfInput := new(LogfileInput) lfiConfig := lfInput.ConfigStruct().(*LogfileInputConfig) lfiConfig.SeekJournalName = "protobuf-seekjournal" lfiConfig.LogFile = "../testsupport/protobuf.log" lfiConfig.ParserType = "message.proto" lfiConfig.UseSeekJournal = true lfiConfig.Decoder = "decoder-name" lfiConfig.DiscoverInterval = 1 lfiConfig.StatInterval = 1 err := lfInput.Init(lfiConfig) c.Expect(err, gs.IsNil) mockDecoderRunner := pipelinemock.NewMockDecoderRunner(ctrl) // Create pool of packs. numLines := 7 // # of lines in the log file we're parsing. packs := make([]*PipelinePack, numLines) ith.PackSupply = make(chan *PipelinePack, numLines) for i := 0; i < numLines; i++ { packs[i] = NewPipelinePack(ith.PackSupply) ith.PackSupply <- packs[i] } c.Specify("reads a log file", func() { // Expect InputRunner calls to get InChan and decode outgoing msgs ith.MockInputRunner.EXPECT().LogError(gomock.Any()).AnyTimes() ith.MockInputRunner.EXPECT().LogMessage(gomock.Any()).AnyTimes() ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply).Times(numLines) ith.MockInputRunner.EXPECT().Name().Return("LogfileInput") pbcall := ith.MockHelper.EXPECT().DecoderRunner(lfiConfig.Decoder, "LogfileInput-"+lfiConfig.Decoder) pbcall.Return(mockDecoderRunner, true) decodeCall := mockDecoderRunner.EXPECT().InChan().Times(numLines) decodeCall.Return(ith.DecodeChan) go func() { err = lfInput.Run(ith.MockInputRunner, ith.MockHelper) c.Expect(err, gs.IsNil) }() for x := 0; x < numLines; x++ { _ = <-ith.DecodeChan // Free up the scheduler while we wait for the log file lines // to be processed. runtime.Gosched() } lfInput.Stop() lines := []int{36230, 41368, 42310, 41343, 37171, 56727, 46492} for i, line := range lines { c.Expect(len(packs[i].MsgBytes), gs.Equals, line) err = proto.Unmarshal(packs[i].MsgBytes, packs[i].Message) c.Expect(err, gs.IsNil) c.Expect(packs[i].Message.GetType(), gs.Equals, "hekabench") } // Wait for the file update to hit the disk; better suggestions are welcome runtime.Gosched() time.Sleep(time.Millisecond * 250) journalData := []byte(`{"last_hash":"f67dc6bbbbb6a91b59e661b6170de50c96eab100","last_len":46499,"last_start":255191,"seek":301690}`) journalFile, err := ioutil.ReadFile(filepath.Join(tmpDir, "seekjournals", lfiConfig.SeekJournalName)) c.Expect(err, gs.IsNil) c.Expect(bytes.Compare(journalData, journalFile), gs.Equals, 0) }) }) c.Specify("A message.proto LogFileInput no decoder", func() { tmpDir, tmpErr := ioutil.TempDir("", "hekad-tests-") c.Expect(tmpErr, gs.Equals, nil) origBaseDir := Globals().BaseDir Globals().BaseDir = tmpDir defer func() { Globals().BaseDir = origBaseDir tmpErr = os.RemoveAll(tmpDir) c.Expect(tmpErr, gs.Equals, nil) }() lfInput := new(LogfileInput) lfiConfig := lfInput.ConfigStruct().(*LogfileInputConfig) lfiConfig.SeekJournalName = "protobuf-seekjournal" lfiConfig.LogFile = "../testsupport/protobuf.log" lfiConfig.ParserType = "message.proto" lfiConfig.UseSeekJournal = true lfiConfig.Decoder = "" lfiConfig.DiscoverInterval = 1 lfiConfig.StatInterval = 1 err := lfInput.Init(lfiConfig) c.Expect(err, gs.Not(gs.IsNil)) }) }
func HttpInputSpec(c gs.Context) { t := &pipeline_ts.SimpleT{} ctrl := gomock.NewController(t) defer ctrl.Finish() pConfig := NewPipelineConfig(nil) json_post := `{"uuid": "xxBI3zyeXU+spG8Uiveumw==", "timestamp": 1372966886023588, "hostname": "Victors-MacBook-Air.local", "pid": 40183, "fields": [{"representation": "", "value_type": "STRING", "name": "cef_meta.syslog_priority", "value_string": [""]}, {"representation": "", "value_type": "STRING", "name": "cef_meta.syslog_ident", "value_string": [""]}, {"representation": "", "value_type": "STRING", "name": "cef_meta.syslog_facility", "value_string": [""]}, {"representation": "", "value_type": "STRING", "name": "cef_meta.syslog_options", "value_string": [""]}], "logger": "", "env_version": "0.8", "type": "cef", "payload": "Jul 04 15:41:26 Victors-MacBook-Air.local CEF:0|mozilla|weave|3|xx\\\\|x|xx\\\\|x|5|cs1Label=requestClientApplication cs1=MySuperBrowser requestMethod=GET request=/ src=127.0.0.1 dest=127.0.0.1 suser=none", "severity": 6}'` c.Specify("A HttpInput", func() { httpInput := HttpInput{} ith := new(plugins_ts.InputTestHelper) ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) ith.MockSplitterRunner = pipelinemock.NewMockSplitterRunner(ctrl) runOutputChan := make(chan error, 1) startInput := func() { go func() { runOutputChan <- httpInput.Run(ith.MockInputRunner, ith.MockHelper) }() } ith.Pack = NewPipelinePack(pConfig.InputRecycleChan()) // These assume that every sub-spec starts the input. config := httpInput.ConfigStruct().(*HttpInputConfig) tickChan := make(chan time.Time) ith.MockInputRunner.EXPECT().Ticker().Return(tickChan) ith.MockHelper.EXPECT().Hostname().Return("hekatests.example.com") // These assume that every sub-spec makes exactly one HTTP request. ith.MockInputRunner.EXPECT().NewSplitterRunner("0").Return(ith.MockSplitterRunner) getRecCall := ith.MockSplitterRunner.EXPECT().GetRecordFromStream(gomock.Any()) getRecCall.Return(len(json_post), []byte(json_post), io.EOF) ith.MockSplitterRunner.EXPECT().UseMsgBytes().Return(false) decChan := make(chan func(*PipelinePack), 1) packDecCall := ith.MockSplitterRunner.EXPECT().SetPackDecorator(gomock.Any()) packDecCall.Do(func(dec func(*PipelinePack)) { decChan <- dec }) ith.MockSplitterRunner.EXPECT().DeliverRecord([]byte(json_post), nil) ith.MockSplitterRunner.EXPECT().IncompleteFinal().Return(false).AnyTimes() splitter := &TokenSplitter{} // not actually used ith.MockSplitterRunner.EXPECT().Splitter().Return(splitter) c.Specify("honors time ticker to flush", func() { // Spin up a http server. server, err := plugins_ts.NewOneHttpServer(json_post, "localhost", 9876) c.Expect(err, gs.IsNil) go server.Start("/") time.Sleep(10 * time.Millisecond) config.Url = "http://localhost:9876/" err = httpInput.Init(config) c.Assume(err, gs.IsNil) startInput() tickChan <- time.Now() // Getting the decorator means we've made our HTTP request. <-decChan }) c.Specify("supports configuring HTTP Basic Authentication", func() { // Spin up a http server which expects username "user" and password "password" server, err := plugins_ts.NewHttpBasicAuthServer("user", "password", "localhost", 9875) c.Expect(err, gs.IsNil) go server.Start("/BasicAuthTest") time.Sleep(10 * time.Millisecond) config.Url = "http://localhost:9875/BasicAuthTest" config.User = "******" config.Password = "******" err = httpInput.Init(config) c.Assume(err, gs.IsNil) startInput() tickChan <- time.Now() dec := <-decChan dec(ith.Pack) // we expect a statuscode 200 (i.e. success) statusCode, ok := ith.Pack.Message.GetFieldValue("StatusCode") c.Assume(ok, gs.IsTrue) c.Expect(statusCode, gs.Equals, int64(200)) }) c.Specify("supports configuring a different HTTP method", func() { // Spin up a http server which expects requests with method "POST" server, err := plugins_ts.NewHttpMethodServer("POST", "localhost", 9874) c.Expect(err, gs.IsNil) go server.Start("/PostTest") time.Sleep(10 * time.Millisecond) config.Url = "http://localhost:9874/PostTest" config.Method = "POST" err = httpInput.Init(config) c.Assume(err, gs.IsNil) startInput() tickChan <- time.Now() dec := <-decChan dec(ith.Pack) // we expect a statuscode 200 (i.e. success) statusCode, ok := ith.Pack.Message.GetFieldValue("StatusCode") c.Assume(ok, gs.IsTrue) c.Expect(statusCode, gs.Equals, int64(200)) }) c.Specify("supports configuring HTTP headers", func() { // Spin up a http server which expects requests with method "POST" server, err := plugins_ts.NewHttpHeadersServer(map[string]string{"Accept": "text/plain"}, "localhost", 9873) c.Expect(err, gs.IsNil) go server.Start("/HeadersTest") time.Sleep(10 * time.Millisecond) config.Url = "http://localhost:9873/HeadersTest" config.Headers = map[string]string{"Accept": "text/plain"} err = httpInput.Init(config) c.Assume(err, gs.IsNil) startInput() tickChan <- time.Now() dec := <-decChan dec(ith.Pack) // we expect a statuscode 200 (i.e. success) statusCode, ok := ith.Pack.Message.GetFieldValue("StatusCode") c.Assume(ok, gs.IsTrue) c.Expect(statusCode, gs.Equals, int64(200)) }) c.Specify("supports configuring a request body", func() { // Spin up a http server that echoes back the request body server, err := plugins_ts.NewHttpBodyServer("localhost", 9872) c.Expect(err, gs.IsNil) go server.Start("/BodyTest") time.Sleep(10 * time.Millisecond) config.Url = "http://localhost:9872/BodyTest" config.Method = "POST" config.Body = json_post err = httpInput.Init(config) c.Assume(err, gs.IsNil) respBodyChan := make(chan []byte, 1) getRecCall.Do(func(r io.Reader) { respBody := make([]byte, len(config.Body)) n, err := r.Read(respBody) c.Expect(n, gs.Equals, len(config.Body)) c.Expect(err, gs.Equals, io.EOF) respBodyChan <- respBody }) startInput() tickChan <- time.Now() respBody := <-respBodyChan c.Expect(string(respBody), gs.Equals, json_post) }) httpInput.Stop() runOutput := <-runOutputChan c.Expect(runOutput, gs.IsNil) }) }
func HttpListenInputSpec(c gs.Context) { t := &pipeline_ts.SimpleT{} ctrl := gomock.NewController(t) defer ctrl.Finish() pConfig := NewPipelineConfig(nil) httpListenInput := HttpListenInput{} ith := new(plugins_ts.InputTestHelper) ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) startInput := func() { go func() { httpListenInput.Run(ith.MockInputRunner, ith.MockHelper) }() } ith.Pack = NewPipelinePack(pConfig.InputRecycleChan()) ith.PackSupply = make(chan *PipelinePack, 1) config := httpListenInput.ConfigStruct().(*HttpListenInputConfig) config.Decoder = "PayloadJsonDecoder" mockDecoderRunner := pipelinemock.NewMockDecoderRunner(ctrl) dRunnerInChan := make(chan *PipelinePack, 1) c.Specify("A HttpListenInput", func() { mockDecoderRunner.EXPECT().InChan().Return(dRunnerInChan) ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply) ith.MockInputRunner.EXPECT().Name().Return("HttpListenInput").Times(2) ith.MockHelper.EXPECT().DecoderRunner("PayloadJsonDecoder", "HttpListenInput-PayloadJsonDecoder").Return(mockDecoderRunner, true) ith.MockHelper.EXPECT().PipelineConfig().Return(pConfig) startedChan := make(chan bool, 1) defer close(startedChan) ts := httptest.NewUnstartedServer(nil) httpListenInput.starterFunc = func(hli *HttpListenInput) error { ts.Start() startedChan <- true return nil } c.Specify("Adds query parameters to the message pack as fields", func() { err := httpListenInput.Init(config) ts.Config = httpListenInput.server c.Assume(err, gs.IsNil) startInput() ith.PackSupply <- ith.Pack <-startedChan resp, err := http.Get(ts.URL + "/?test=Hello%20World") c.Assume(err, gs.IsNil) resp.Body.Close() c.Assume(resp.StatusCode, gs.Equals, 200) pack := <-dRunnerInChan fieldValue, ok := pack.Message.GetFieldValue("test") c.Assume(ok, gs.IsTrue) c.Expect(fieldValue, gs.Equals, "Hello World") }) c.Specify("Add custom headers", func() { config.Headers = http.Header{ "One": []string{"two", "three"}, "Four": []string{"five", "six", "seven"}, } err := httpListenInput.Init(config) ts.Config = httpListenInput.server c.Assume(err, gs.IsNil) startInput() ith.PackSupply <- ith.Pack <-startedChan resp, err := http.Get(ts.URL) c.Assume(err, gs.IsNil) resp.Body.Close() c.Assume(resp.StatusCode, gs.Equals, 200) <-dRunnerInChan // Verify headers are there eq := reflect.DeepEqual(resp.Header["One"], config.Headers["One"]) c.Expect(eq, gs.IsTrue) eq = reflect.DeepEqual(resp.Header["Four"], config.Headers["Four"]) c.Expect(eq, gs.IsTrue) }) ts.Close() httpListenInput.Stop() }) }
func DashboardOutputSpec(c gs.Context) { t := new(pipeline_ts.SimpleT) ctrl := gomock.NewController(t) defer ctrl.Finish() pConfig := pipeline.NewPipelineConfig(nil) dashboardOutput := new(DashboardOutput) dashboardOutput.pConfig = pConfig oth := plugins_ts.NewOutputTestHelper(ctrl) oth.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) oth.MockOutputRunner = pipelinemock.NewMockOutputRunner(ctrl) errChan := make(chan error, 1) startOutput := func() { go func() { errChan <- dashboardOutput.Run(oth.MockOutputRunner, oth.MockHelper) }() } if runtime.GOOS != "windows" { c.Specify("A DashboardOutput", func() { tmpdir, err := ioutil.TempDir("", "dashboard_output_test") c.Assume(err, gs.IsNil) config := dashboardOutput.ConfigStruct().(*DashboardOutputConfig) config.WorkingDirectory = tmpdir c.Specify("Init halts if basedirectory is not writable", func() { err := os.MkdirAll(tmpdir, 0400) c.Assume(err, gs.IsNil) defer os.RemoveAll(tmpdir) err = dashboardOutput.Init(config) c.Assume(err, gs.Not(gs.IsNil)) }) c.Specify("that is running", func() { startedChan := make(chan bool, 1) defer close(startedChan) ts := httptest.NewUnstartedServer(nil) dashboardOutput.starterFunc = func(hli *DashboardOutput) error { ts.Start() startedChan <- true return nil } ticker := make(chan time.Time) inChan := make(chan *pipeline.PipelinePack, 1) recycleChan := make(chan *pipeline.PipelinePack, 1) pack := pipeline.NewPipelinePack(recycleChan) pack.Message = pipeline_ts.GetTestMessage() oth.MockOutputRunner.EXPECT().InChan().Return(inChan) oth.MockOutputRunner.EXPECT().Ticker().Return(ticker) err := os.MkdirAll(tmpdir, 0700) c.Assume(err, gs.IsNil) defer os.RemoveAll(tmpdir) dashboardOutput.handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Noop }) c.Specify("sets custom http headers", func() { config.Headers = http.Header{ "One": []string{"two", "three"}, "Four": []string{"five", "six", "seven"}, } err = dashboardOutput.Init(config) c.Assume(err, gs.IsNil) ts.Config = dashboardOutput.server startOutput() inChan <- pack <-startedChan resp, err := http.Get(ts.URL) c.Assume(err, gs.IsNil) resp.Body.Close() c.Assume(resp.StatusCode, gs.Equals, 200) // Verify headers are there eq := reflect.DeepEqual(resp.Header["One"], config.Headers["One"]) c.Expect(eq, gs.IsTrue) eq = reflect.DeepEqual(resp.Header["Four"], config.Headers["Four"]) c.Expect(eq, gs.IsTrue) }) close(inChan) c.Expect(<-errChan, gs.IsNil) ts.Close() }) }) } }
func TcpInputSpec(c gs.Context) { t := &pipeline_ts.SimpleT{} ctrl := gomock.NewController(t) defer ctrl.Finish() pConfig := NewPipelineConfig(nil) ith := new(plugins_ts.InputTestHelper) ith.Msg = pipeline_ts.GetTestMessage() ith.Pack = NewPipelinePack(pConfig.InputRecycleChan()) ith.AddrStr = "localhost:55565" ith.ResolvedAddrStr = "127.0.0.1:55565" ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) ith.MockDeliverer = pipelinemock.NewMockDeliverer(ctrl) ith.MockSplitterRunner = pipelinemock.NewMockSplitterRunner(ctrl) ith.PackSupply = make(chan *PipelinePack, 1) c.Specify("A TcpInput", func() { tcpInput := &TcpInput{} config := &TcpInputConfig{ Net: "tcp", Address: ith.AddrStr, } bytesChan := make(chan []byte, 1) errChan := make(chan error, 1) var srDoneWG sync.WaitGroup startServer := func() { srDoneWG.Add(1) ith.MockInputRunner.EXPECT().Name().Return("mock_name") ith.MockInputRunner.EXPECT().NewDeliverer(gomock.Any()).Return(ith.MockDeliverer) ith.MockDeliverer.EXPECT().Done() ith.MockInputRunner.EXPECT().NewSplitterRunner(gomock.Any()).Return( ith.MockSplitterRunner) ith.MockSplitterRunner.EXPECT().UseMsgBytes().Return(false) ith.MockSplitterRunner.EXPECT().SetPackDecorator(gomock.Any()) ith.MockSplitterRunner.EXPECT().Done().Do(func() { srDoneWG.Done() }) // splitCall gets called twice. The first time it returns nil, the // second time it returns io.EOF. splitCall := ith.MockSplitterRunner.EXPECT().SplitStream(gomock.Any(), ith.MockDeliverer).AnyTimes() splitCall.Do(func(conn net.Conn, del Deliverer) { recd, _ := ioutil.ReadAll(conn) bytesChan <- recd splitCall.Return(io.EOF) }) err := tcpInput.Run(ith.MockInputRunner, ith.MockHelper) errChan <- err } c.Specify("not using TLS", func() { err := tcpInput.Init(config) c.Assume(err, gs.IsNil) c.Expect(tcpInput.listener.Addr().String(), gs.Equals, ith.ResolvedAddrStr) c.Specify("accepts connections and passes them to the splitter", func() { go startServer() data := []byte("THIS IS THE DATA") outConn, err := net.Dial("tcp", ith.AddrStr) c.Assume(err, gs.IsNil) _, err = outConn.Write(data) c.Expect(err, gs.IsNil) outConn.Close() recd := <-bytesChan c.Expect(err, gs.IsNil) c.Expect(string(recd), gs.Equals, string(data)) tcpInput.Stop() err = <-errChan c.Expect(err, gs.IsNil) srDoneWG.Wait() }) }) c.Specify("using TLS", func() { config.UseTls = true c.Specify("fails to init w/ missing key or cert file", func() { config.Tls = TlsConfig{} err := tcpInput.Init(config) c.Expect(err, gs.Not(gs.IsNil)) c.Expect(err.Error(), gs.Equals, "TLS config requires both cert_file and key_file value.") }) config.Tls = TlsConfig{ CertFile: "./testsupport/cert.pem", KeyFile: "./testsupport/key.pem", } c.Specify("accepts connections and passes them to the splitter", func() { err := tcpInput.Init(config) c.Expect(err, gs.IsNil) go startServer() data := []byte("This is a test.") clientConfig := new(tls.Config) clientConfig.InsecureSkipVerify = true outConn, err := tls.Dial("tcp", ith.AddrStr, clientConfig) c.Assume(err, gs.IsNil) outConn.SetWriteDeadline(time.Now().Add(time.Duration(10000))) n, err := outConn.Write(data) c.Expect(err, gs.IsNil) c.Expect(n, gs.Equals, len(data)) outConn.Close() recd := <-bytesChan c.Expect(err, gs.IsNil) c.Expect(string(recd), gs.Equals, string(data)) tcpInput.Stop() err = <-errChan c.Expect(err, gs.IsNil) srDoneWG.Wait() }) c.Specify("doesn't accept connections below specified min TLS version", func() { config.Tls.MinVersion = "TLS12" err := tcpInput.Init(config) c.Expect(err, gs.IsNil) go startServer() clientConfig := &tls.Config{ InsecureSkipVerify: true, MaxVersion: tls.VersionTLS11, } conn, err := tls.Dial("tcp", ith.AddrStr, clientConfig) c.Expect(conn, gs.IsNil) c.Expect(err, gs.Not(gs.IsNil)) <-bytesChan tcpInput.Stop() err = <-errChan c.Expect(err, gs.IsNil) srDoneWG.Wait() }) }) }) }
func ProcessDirectoryInputSpec(c gs.Context) { t := &pipeline_ts.SimpleT{} ctrl := gomock.NewController(t) defer ctrl.Finish() pConfig := NewPipelineConfig(nil) ith := new(plugins_ts.InputTestHelper) ith.Msg = pipeline_ts.GetTestMessage() ith.Pack = NewPipelinePack(pConfig.InputRecycleChan()) // set up mock helper, decoder set, and packSupply channel ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) ith.MockDeliverer = pipelinemock.NewMockDeliverer(ctrl) ith.MockSplitterRunner = pipelinemock.NewMockSplitterRunner(ctrl) ith.PackSupply = make(chan *PipelinePack, 1) ith.PackSupply <- ith.Pack err := pConfig.RegisterDefault("NullSplitter") c.Assume(err, gs.IsNil) c.Specify("A ProcessDirectoryInput", func() { pdiInput := ProcessDirectoryInput{} pdiInput.SetPipelineConfig(pConfig) config := pdiInput.ConfigStruct().(*ProcessDirectoryInputConfig) workingDir, err := os.Getwd() c.Assume(err, gs.IsNil) config.ProcessDir = filepath.Join(workingDir, "testsupport", "processes") // `Ticker` is the last thing called during the setup part of the // input's `Run` method, so it triggers a waitgroup that tests can // wait on when they need to ensure initialization has finished. var started sync.WaitGroup started.Add(1) tickChan := make(chan time.Time, 1) ith.MockInputRunner.EXPECT().Ticker().Return(tickChan).Do( func() { started.Done() }) // Similarly we use a waitgroup to signal when LogMessage has been // called to know when reloads have completed. Warning: If you call // expectLogMessage with a msg that is never passed to LogMessage and // then you call loaded.Wait() then your test will hang and never // complete. var loaded sync.WaitGroup expectLogMessage := func(msg string) { loaded.Add(1) ith.MockInputRunner.EXPECT().LogMessage(msg).Do( func(msg string) { loaded.Done() }) } // Same name => same content. paths := []string{ filepath.Join(config.ProcessDir, "100", "h0.toml"), filepath.Join(config.ProcessDir, "100", "h1.toml"), filepath.Join(config.ProcessDir, "200", "h0.toml"), filepath.Join(config.ProcessDir, "300", "h1.toml"), } copyFile := func(src, dest string) { inFile, err := os.Open(src) c.Assume(err, gs.IsNil) outFile, err := os.Create(dest) c.Assume(err, gs.IsNil) _, err = io.Copy(outFile, inFile) c.Assume(err, gs.IsNil) inFile.Close() outFile.Close() } err = pdiInput.Init(config) c.Expect(err, gs.IsNil) for _, p := range paths { expectLogMessage("Added: " + p) } go pdiInput.Run(ith.MockInputRunner, ith.MockHelper) defer func() { pdiInput.Stop() for _, entry := range pdiInput.inputs { entry.ir.Input().Stop() } }() started.Wait() c.Specify("loads scheduled jobs", func() { pathIndex := func(name string) (i int) { var p string for i, p = range paths { if name == p { return } } return -1 } for name, entry := range pdiInput.inputs { i := pathIndex(name) // Make sure each file path got registered. c.Expect(i, gs.Not(gs.Equals), -1) dirName := filepath.Base(filepath.Dir(name)) dirInt, err := strconv.Atoi(dirName) c.Expect(err, gs.IsNil) // And that the ticker interval was read correctly. c.Expect(uint(dirInt), gs.Equals, entry.config.TickerInterval) } }) c.Specify("discovers and adds a new job", func() { // Copy one of the files to register a new process. newPath := filepath.Join(config.ProcessDir, "300", "h0.toml") copyFile(paths[0], newPath) defer func() { err := os.Remove(newPath) c.Assume(err, gs.IsNil) }() // Set up expectations and trigger process dir reload. expectLogMessage("Added: " + newPath) tickChan <- time.Now() loaded.Wait() // Make sure our plugin was loaded. c.Expect(len(pdiInput.inputs), gs.Equals, 5) newEntry, ok := pdiInput.inputs[newPath] c.Expect(ok, gs.IsTrue) c.Expect(newEntry.config.TickerInterval, gs.Equals, uint(300)) }) c.Specify("removes a deleted job", func() { err := os.Remove(paths[3]) c.Assume(err, gs.IsNil) defer func() { copyFile(paths[1], paths[3]) }() // Set up expectations and trigger process dir reload. expectLogMessage("Removed: " + paths[3]) tickChan <- time.Now() loaded.Wait() // Make sure our plugin was deleted. c.Expect(len(pdiInput.inputs), gs.Equals, 3) }) c.Specify("notices a changed job", func() { // Overwrite one job w/ a slightly different one. copyFile(paths[0], paths[3]) defer copyFile(paths[1], paths[3]) // Set up expectations and trigger process dir reload. expectLogMessage("Removed: " + paths[3]) expectLogMessage("Added: " + paths[3]) tickChan <- time.Now() loaded.Wait() // Make sure the new config was loaded. c.Expect(pdiInput.inputs[paths[3]].config.Command["0"].Args[0], gs.Equals, "hello world\n") }) }) }
func UdpInputSpec(c gs.Context) { t := &pipeline_ts.SimpleT{} ctrl := gomock.NewController(t) defer ctrl.Finish() config := NewPipelineConfig(nil) ith := new(plugins_ts.InputTestHelper) ith.Msg = pipeline_ts.GetTestMessage() ith.Pack = NewPipelinePack(config.InputRecycleChan()) // set up mock helper, decoder set, and packSupply channel ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) ith.MockSplitterRunner = pipelinemock.NewMockSplitterRunner(ctrl) c.Specify("A UdpInput", func() { udpInput := UdpInput{} config := &UdpInputConfig{} mbytes, _ := proto.Marshal(ith.Msg) header := &message.Header{} header.SetMessageLength(uint32(len(mbytes))) hbytes, _ := proto.Marshal(header) buf := encodeMessage(hbytes, mbytes) bytesChan := make(chan []byte, 1) ith.MockInputRunner.EXPECT().Name().Return("mock_name") ith.MockInputRunner.EXPECT().NewSplitterRunner("").Return(ith.MockSplitterRunner) ith.MockSplitterRunner.EXPECT().GetRemainingData().AnyTimes() ith.MockSplitterRunner.EXPECT().UseMsgBytes().Return(false) ith.MockSplitterRunner.EXPECT().SetPackDecorator(gomock.Any()) splitCall := ith.MockSplitterRunner.EXPECT().SplitStream(gomock.Any(), nil).AnyTimes() splitCall.Do(func(conn net.Conn, del Deliverer) { recd := make([]byte, 65536) n, _ := conn.Read(recd) recd = recd[:n] bytesChan <- recd }) c.Specify("using a udp address", func() { ith.AddrStr = "localhost:55565" ith.ResolvedAddrStr = "127.0.0.1:55565" config.Net = "udp" config.Address = ith.AddrStr err := udpInput.Init(config) c.Assume(err, gs.IsNil) realListener := (udpInput.listener).(*net.UDPConn) c.Expect(realListener.LocalAddr().String(), gs.Equals, ith.ResolvedAddrStr) c.Specify("passes the connection to SplitStream", func() { go udpInput.Run(ith.MockInputRunner, ith.MockHelper) conn, err := net.Dial("udp", ith.AddrStr) c.Assume(err, gs.IsNil) _, err = conn.Write(buf) c.Assume(err, gs.IsNil) conn.Close() recd := <-bytesChan c.Expect(string(recd), gs.Equals, string(buf)) udpInput.Stop() }) }) if runtime.GOOS != "windows" { c.Specify("using a unix datagram socket", func() { tmpDir, err := ioutil.TempDir("", "heka-socket") c.Assume(err, gs.IsNil) unixPath := filepath.Join(tmpDir, "unixgram-socket") ith.AddrStr = unixPath config.Net = "unixgram" config.Address = ith.AddrStr err = udpInput.Init(config) c.Assume(err, gs.IsNil) realListener := (udpInput.listener).(*net.UnixConn) c.Expect(realListener.LocalAddr().String(), gs.Equals, unixPath) c.Specify("passes the socket to SplitStream", func() { go udpInput.Run(ith.MockInputRunner, ith.MockHelper) unixAddr, err := net.ResolveUnixAddr("unixgram", unixPath) c.Assume(err, gs.IsNil) conn, err := net.DialUnix("unixgram", nil, unixAddr) c.Assume(err, gs.IsNil) _, err = conn.Write(buf) c.Assume(err, gs.IsNil) conn.Close() recd := <-bytesChan c.Expect(string(recd), gs.Equals, string(buf)) udpInput.Stop() }) }) } }) }
func TcpInputSpec(c gs.Context) { t := &pipeline_ts.SimpleT{} ctrl := gomock.NewController(t) defer ctrl.Finish() config := NewPipelineConfig(nil) ith := new(plugins_ts.InputTestHelper) ith.Msg = pipeline_ts.GetTestMessage() ith.Pack = NewPipelinePack(config.InputRecycleChan()) ith.AddrStr = "localhost:55565" ith.ResolvedAddrStr = "127.0.0.1:55565" // set up mock helper, decoder set, and packSupply channel ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) ith.Decoder = pipelinemock.NewMockDecoderRunner(ctrl) ith.PackSupply = make(chan *PipelinePack, 1) ith.DecodeChan = make(chan *PipelinePack) key := "testkey" signers := map[string]Signer{"test_1": {key}} signer := "test" c.Specify("A TcpInput protobuf parser", func() { ith.MockInputRunner.EXPECT().Name().Return("TcpInput") tcpInput := TcpInput{} err := tcpInput.Init(&TcpInputConfig{Net: "tcp", Address: ith.AddrStr, Signers: signers, Decoder: "ProtobufDecoder", ParserType: "message.proto"}) c.Assume(err, gs.IsNil) realListener := tcpInput.listener c.Expect(realListener.Addr().String(), gs.Equals, ith.ResolvedAddrStr) realListener.Close() mockConnection := pipeline_ts.NewMockConn(ctrl) mockListener := pipeline_ts.NewMockListener(ctrl) tcpInput.listener = mockListener addr := new(address) addr.str = "123" mockConnection.EXPECT().RemoteAddr().Return(addr) mbytes, _ := proto.Marshal(ith.Msg) header := &message.Header{} header.SetMessageLength(uint32(len(mbytes))) err = errors.New("connection closed") // used in the read return(s) readCall := mockConnection.EXPECT().Read(gomock.Any()) readEnd := mockConnection.EXPECT().Read(gomock.Any()).After(readCall) readEnd.Return(0, err) mockConnection.EXPECT().SetReadDeadline(gomock.Any()).Return(nil).AnyTimes() mockConnection.EXPECT().Close() neterr := pipeline_ts.NewMockError(ctrl) neterr.EXPECT().Temporary().Return(false) acceptCall := mockListener.EXPECT().Accept().Return(mockConnection, nil) acceptCall.Do(func() { acceptCall = mockListener.EXPECT().Accept() acceptCall.Return(nil, neterr) }) mockDecoderRunner := ith.Decoder.(*pipelinemock.MockDecoderRunner) mockDecoderRunner.EXPECT().InChan().Return(ith.DecodeChan) ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply) enccall := ith.MockHelper.EXPECT().DecoderRunner("ProtobufDecoder", "TcpInput-123-ProtobufDecoder").AnyTimes() enccall.Return(ith.Decoder, true) ith.MockHelper.EXPECT().StopDecoderRunner(ith.Decoder) cleanup := func() { mockListener.EXPECT().Close() tcpInput.Stop() tcpInput.wg.Wait() } c.Specify("reads a message from its connection", func() { hbytes, _ := proto.Marshal(header) buflen := 3 + len(hbytes) + len(mbytes) readCall.Return(buflen, nil) readCall.Do(getPayloadBytes(hbytes, mbytes)) go tcpInput.Run(ith.MockInputRunner, ith.MockHelper) defer cleanup() ith.PackSupply <- ith.Pack packRef := <-ith.DecodeChan c.Expect(ith.Pack, gs.Equals, packRef) c.Expect(string(ith.Pack.MsgBytes), gs.Equals, string(mbytes)) }) c.Specify("reads a MD5 signed message from its connection", func() { header.SetHmacHashFunction(message.Header_MD5) header.SetHmacSigner(signer) header.SetHmacKeyVersion(uint32(1)) hm := hmac.New(md5.New, []byte(key)) hm.Write(mbytes) header.SetHmac(hm.Sum(nil)) hbytes, _ := proto.Marshal(header) buflen := 3 + len(hbytes) + len(mbytes) readCall.Return(buflen, nil) readCall.Do(getPayloadBytes(hbytes, mbytes)) go tcpInput.Run(ith.MockInputRunner, ith.MockHelper) defer cleanup() ith.PackSupply <- ith.Pack timeout := make(chan bool, 1) go func() { time.Sleep(100 * time.Millisecond) timeout <- true }() select { case packRef := <-ith.DecodeChan: c.Expect(ith.Pack, gs.Equals, packRef) c.Expect(string(ith.Pack.MsgBytes), gs.Equals, string(mbytes)) c.Expect(ith.Pack.Signer, gs.Equals, "test") case t := <-timeout: c.Expect(t, gs.IsNil) } }) c.Specify("reads a SHA1 signed message from its connection", func() { header.SetHmacHashFunction(message.Header_SHA1) header.SetHmacSigner(signer) header.SetHmacKeyVersion(uint32(1)) hm := hmac.New(sha1.New, []byte(key)) hm.Write(mbytes) header.SetHmac(hm.Sum(nil)) hbytes, _ := proto.Marshal(header) buflen := 3 + len(hbytes) + len(mbytes) readCall.Return(buflen, nil) readCall.Do(getPayloadBytes(hbytes, mbytes)) go tcpInput.Run(ith.MockInputRunner, ith.MockHelper) defer cleanup() ith.PackSupply <- ith.Pack timeout := make(chan bool, 1) go func() { time.Sleep(100 * time.Millisecond) timeout <- true }() select { case packRef := <-ith.DecodeChan: c.Expect(ith.Pack, gs.Equals, packRef) c.Expect(string(ith.Pack.MsgBytes), gs.Equals, string(mbytes)) c.Expect(ith.Pack.Signer, gs.Equals, "test") case t := <-timeout: c.Expect(t, gs.IsNil) } }) c.Specify("reads a signed message with an expired key from its connection", func() { header.SetHmacHashFunction(message.Header_MD5) header.SetHmacSigner(signer) header.SetHmacKeyVersion(uint32(11)) // non-existent key version hm := hmac.New(md5.New, []byte(key)) hm.Write(mbytes) header.SetHmac(hm.Sum(nil)) hbytes, _ := proto.Marshal(header) buflen := 3 + len(hbytes) + len(mbytes) readCall.Return(buflen, nil) readCall.Do(getPayloadBytes(hbytes, mbytes)) go tcpInput.Run(ith.MockInputRunner, ith.MockHelper) defer cleanup() ith.PackSupply <- ith.Pack timeout := make(chan bool, 1) go func() { time.Sleep(100 * time.Millisecond) timeout <- true }() select { case packRef := <-mockDecoderRunner.InChan(): c.Expect(packRef, gs.IsNil) case t := <-timeout: c.Expect(t, gs.IsTrue) } }) c.Specify("reads a signed message with an incorrect hmac from its connection", func() { header.SetHmacHashFunction(message.Header_MD5) header.SetHmacSigner(signer) header.SetHmacKeyVersion(uint32(1)) hm := hmac.New(md5.New, []byte(key)) hm.Write([]byte("some bytes")) header.SetHmac(hm.Sum(nil)) hbytes, _ := proto.Marshal(header) buflen := 3 + len(hbytes) + len(mbytes) readCall.Return(buflen, nil) readCall.Do(getPayloadBytes(hbytes, mbytes)) go tcpInput.Run(ith.MockInputRunner, ith.MockHelper) defer cleanup() ith.PackSupply <- ith.Pack timeout := make(chan bool, 1) go func() { time.Sleep(100 * time.Millisecond) timeout <- true }() select { case packRef := <-mockDecoderRunner.InChan(): c.Expect(packRef, gs.IsNil) case t := <-timeout: c.Expect(t, gs.IsTrue) } }) }) c.Specify("A TcpInput regexp parser", func() { ith.MockInputRunner.EXPECT().Name().Return("TcpInput") config := &TcpInputConfig{ Net: "tcp", Address: ith.AddrStr, Decoder: "RegexpDecoder", ParserType: "regexp", } tcpInput := TcpInput{} err := tcpInput.Init(config) c.Assume(err, gs.IsNil) realListener := tcpInput.listener c.Expect(realListener.Addr().String(), gs.Equals, ith.ResolvedAddrStr) realListener.Close() mockConnection := pipeline_ts.NewMockConn(ctrl) mockListener := pipeline_ts.NewMockListener(ctrl) tcpInput.listener = mockListener addr := new(address) addr.str = "123" mockConnection.EXPECT().RemoteAddr().Return(addr).Times(2) mbytes := []byte("this is a test message\n") err = errors.New("connection closed") // used in the read return(s) readCall := mockConnection.EXPECT().Read(gomock.Any()) readEnd := mockConnection.EXPECT().Read(gomock.Any()).After(readCall) readEnd.Return(0, err) mockConnection.EXPECT().SetReadDeadline(gomock.Any()).Return(nil).AnyTimes() mockConnection.EXPECT().Close() neterr := pipeline_ts.NewMockError(ctrl) neterr.EXPECT().Temporary().Return(false) acceptCall := mockListener.EXPECT().Accept().Return(mockConnection, nil) acceptCall.Do(func() { acceptCall = mockListener.EXPECT().Accept() acceptCall.Return(nil, neterr) }) mockDecoderRunner := ith.Decoder.(*pipelinemock.MockDecoderRunner) mockDecoderRunner.EXPECT().InChan().Return(ith.DecodeChan) ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply) ith.MockInputRunner.EXPECT().Name().Return("logger") enccall := ith.MockHelper.EXPECT().DecoderRunner("RegexpDecoder", "TcpInput-123-RegexpDecoder").AnyTimes() enccall.Return(ith.Decoder, true) ith.MockHelper.EXPECT().StopDecoderRunner(ith.Decoder) c.Specify("reads a message from its connection", func() { readCall.Return(len(mbytes), nil) readCall.Do(getPayloadText(mbytes)) go tcpInput.Run(ith.MockInputRunner, ith.MockHelper) defer func() { mockListener.EXPECT().Close() tcpInput.Stop() tcpInput.wg.Wait() }() ith.PackSupply <- ith.Pack packRef := <-ith.DecodeChan c.Expect(ith.Pack, gs.Equals, packRef) c.Expect(ith.Pack.Message.GetPayload(), gs.Equals, string(mbytes[:len(mbytes)-1])) c.Expect(ith.Pack.Message.GetLogger(), gs.Equals, "logger") c.Expect(ith.Pack.Message.GetHostname(), gs.Equals, "123") }) }) c.Specify("A TcpInput token parser", func() { ith.MockInputRunner.EXPECT().Name().Return("TcpInput") tcpInput := TcpInput{} err := tcpInput.Init(&TcpInputConfig{Net: "tcp", Address: ith.AddrStr, Decoder: "TokenDecoder", ParserType: "token", Delimiter: "\n"}) c.Assume(err, gs.IsNil) realListener := tcpInput.listener c.Expect(realListener.Addr().String(), gs.Equals, ith.ResolvedAddrStr) realListener.Close() mockConnection := pipeline_ts.NewMockConn(ctrl) mockListener := pipeline_ts.NewMockListener(ctrl) tcpInput.listener = mockListener addr := new(address) addr.str = "123" mockConnection.EXPECT().RemoteAddr().Return(addr).Times(2) mbytes := []byte("this is a test message\n") err = errors.New("connection closed") // used in the read return(s) readCall := mockConnection.EXPECT().Read(gomock.Any()) readEnd := mockConnection.EXPECT().Read(gomock.Any()).After(readCall) readEnd.Return(0, err) mockConnection.EXPECT().SetReadDeadline(gomock.Any()).Return(nil).AnyTimes() mockConnection.EXPECT().Close() neterr := pipeline_ts.NewMockError(ctrl) neterr.EXPECT().Temporary().Return(false) acceptCall := mockListener.EXPECT().Accept().Return(mockConnection, nil) acceptCall.Do(func() { acceptCall = mockListener.EXPECT().Accept() acceptCall.Return(nil, neterr) }) mockDecoderRunner := ith.Decoder.(*pipelinemock.MockDecoderRunner) mockDecoderRunner.EXPECT().InChan().Return(ith.DecodeChan) ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply) ith.MockInputRunner.EXPECT().Name().Return("logger") enccall := ith.MockHelper.EXPECT().DecoderRunner("TokenDecoder", "TcpInput-123-TokenDecoder").AnyTimes() enccall.Return(ith.Decoder, true) ith.MockHelper.EXPECT().StopDecoderRunner(ith.Decoder) c.Specify("reads a message from its connection", func() { readCall.Return(len(mbytes), nil) readCall.Do(getPayloadText(mbytes)) go tcpInput.Run(ith.MockInputRunner, ith.MockHelper) defer func() { mockListener.EXPECT().Close() tcpInput.Stop() tcpInput.wg.Wait() }() ith.PackSupply <- ith.Pack packRef := <-ith.DecodeChan c.Expect(ith.Pack, gs.Equals, packRef) c.Expect(ith.Pack.Message.GetPayload(), gs.Equals, string(mbytes)) c.Expect(ith.Pack.Message.GetLogger(), gs.Equals, "logger") c.Expect(ith.Pack.Message.GetHostname(), gs.Equals, "123") }) }) c.Specify("A TcpInput using TLS", func() { tcpInput := TcpInput{} config := &TcpInputConfig{ Net: "tcp", Address: ith.AddrStr, ParserType: "token", UseTls: true, } c.Specify("fails to init w/ missing key or cert file", func() { config.Tls = TlsConfig{} err := tcpInput.Init(config) c.Expect(err, gs.Not(gs.IsNil)) }) c.Specify("accepts TLS client connections", func() { ith.MockInputRunner.EXPECT().Name().Return("TcpInput") config.Tls = TlsConfig{ CertFile: "./testsupport/cert.pem", KeyFile: "./testsupport/key.pem", } err := tcpInput.Init(config) c.Expect(err, gs.IsNil) go tcpInput.Run(ith.MockInputRunner, ith.MockHelper) defer func() { tcpInput.Stop() tcpInput.wg.Wait() }() clientConfig := new(tls.Config) clientConfig.InsecureSkipVerify = true conn, err := tls.Dial("tcp", ith.AddrStr, clientConfig) c.Expect(err, gs.IsNil) defer conn.Close() conn.SetWriteDeadline(time.Now().Add(time.Duration(10000))) n, err := conn.Write([]byte("This is a test.")) c.Expect(err, gs.IsNil) c.Expect(n, gs.Equals, len("This is a test.")) }) c.Specify("doesn't accept connections below specified min TLS version", func() { ith.MockInputRunner.EXPECT().Name().Return("TcpInput") config.Tls = TlsConfig{ CertFile: "./testsupport/cert.pem", KeyFile: "./testsupport/key.pem", MinVersion: "TLS12", } err := tcpInput.Init(config) c.Expect(err, gs.IsNil) go tcpInput.Run(ith.MockInputRunner, ith.MockHelper) defer func() { tcpInput.Stop() tcpInput.wg.Wait() time.Sleep(time.Duration(1000)) }() clientConfig := &tls.Config{ InsecureSkipVerify: true, MaxVersion: tls.VersionTLS11, } conn, err := tls.Dial("tcp", ith.AddrStr, clientConfig) c.Expect(conn, gs.IsNil) c.Expect(err, gs.Not(gs.IsNil)) }) }) }
func TestReceivePayloadMessage(t *testing.T) { b1 := sarama.NewMockBroker(t, 1) b2 := sarama.NewMockBroker(t, 2) ctrl := gomock.NewController(t) tmpDir, tmpErr := ioutil.TempDir("", "kafkainput-tests") if tmpErr != nil { t.Errorf("Unable to create a temporary directory: %s", tmpErr) } defer func() { if err := os.RemoveAll(tmpDir); err != nil { t.Errorf("Cleanup failed: %s", err) } ctrl.Finish() }() topic := "test" mdr := new(sarama.MetadataResponse) mdr.AddBroker(b2.Addr(), b2.BrokerID()) mdr.AddTopicPartition(topic, 0, 2) b1.Returns(mdr) or := new(sarama.OffsetResponse) or.AddTopicPartition(topic, 0, 0) b2.Returns(or) fr := new(sarama.FetchResponse) fr.AddMessage(topic, 0, nil, sarama.ByteEncoder([]byte{0x41, 0x42}), 0) b2.Returns(fr) pConfig := NewPipelineConfig(nil) pConfig.Globals.BaseDir = tmpDir ki := new(KafkaInput) ki.SetName(topic) ki.SetPipelineConfig(pConfig) config := ki.ConfigStruct().(*KafkaInputConfig) config.Addrs = append(config.Addrs, b1.Addr()) config.Topic = topic ith := new(plugins_ts.InputTestHelper) ith.Pack = NewPipelinePack(pConfig.InputRecycleChan()) ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) ith.MockSplitterRunner = pipelinemock.NewMockSplitterRunner(ctrl) err := ki.Init(config) if err != nil { t.Fatalf("%s", err) } ith.MockInputRunner.EXPECT().NewSplitterRunner("").Return(ith.MockSplitterRunner) ith.MockSplitterRunner.EXPECT().UseMsgBytes().Return(false) decChan := make(chan func(*PipelinePack), 1) decCall := ith.MockSplitterRunner.EXPECT().SetPackDecorator(gomock.Any()) decCall.Do(func(dec func(pack *PipelinePack)) { decChan <- dec }) bytesChan := make(chan []byte, 1) splitCall := ith.MockSplitterRunner.EXPECT().SplitBytes(gomock.Any(), nil) splitCall.Do(func(recd []byte, del Deliverer) { bytesChan <- recd }) errChan := make(chan error) go func() { errChan <- ki.Run(ith.MockInputRunner, ith.MockHelper) }() recd := <-bytesChan if string(recd) != "AB" { t.Errorf("Invalid Payload Expected: AB received: %s", string(recd)) } packDec := <-decChan packDec(ith.Pack) if ith.Pack.Message.GetType() != "heka.kafka" { t.Errorf("Invalid Type %s", ith.Pack.Message.GetType()) } // There is a hang on the consumer close with the mock broker // closing the brokers before the consumer works around the issue // and is good enough for this test. b1.Close() b2.Close() ki.Stop() err = <-errChan if err != nil { t.Fatal(err) } filename := filepath.Join(tmpDir, "kafka", "test.test.0.offset.bin") if o, err := readCheckpoint(filename); err != nil { t.Errorf("Could not read the checkpoint file: %s", filename) } else { if o != 1 { t.Errorf("Incorrect offset Expected: 1 Received: %d", o) } } }
func LogstreamerInputSpec(c gs.Context) { t := &pipeline_ts.SimpleT{} ctrl := gomock.NewController(t) defer ctrl.Finish() here, _ := os.Getwd() dirPath := filepath.Join(here, "../../logstreamer", "testdir", "filehandling/subdir") tmpDir, tmpErr := ioutil.TempDir("", "hekad-tests") c.Expect(tmpErr, gs.Equals, nil) defer func() { tmpErr = os.RemoveAll(tmpDir) c.Expect(tmpErr, gs.IsNil) }() globals := DefaultGlobals() globals.BaseDir = tmpDir config := NewPipelineConfig(globals) ith := new(plugins_ts.InputTestHelper) ith.Msg = pipeline_ts.GetTestMessage() ith.Pack = NewPipelinePack(config.InputRecycleChan()) // Specify localhost, but we're not really going to use the network ith.AddrStr = "localhost:55565" ith.ResolvedAddrStr = "127.0.0.1:55565" // set up mock helper, decoder set, and packSupply channel ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) ith.Decoder = pipelinemock.NewMockDecoderRunner(ctrl) ith.PackSupply = make(chan *PipelinePack, 1) ith.DecodeChan = make(chan *PipelinePack) c.Specify("A LogstreamerInput", func() { lsInput := &LogstreamerInput{pConfig: config} lsiConfig := lsInput.ConfigStruct().(*LogstreamerInputConfig) lsiConfig.LogDirectory = dirPath lsiConfig.FileMatch = `file.log(\.?)(?P<Seq>\d+)?` lsiConfig.Differentiator = []string{"logfile"} lsiConfig.Priority = []string{"^Seq"} lsiConfig.Decoder = "decoder-name" c.Specify("w/ no translation map", func() { err := lsInput.Init(lsiConfig) c.Expect(err, gs.IsNil) c.Expect(len(lsInput.plugins), gs.Equals, 1) mockDecoderRunner := pipelinemock.NewMockDecoderRunner(ctrl) // Create pool of packs. numLines := 5 // # of lines in the log file we're parsing. packs := make([]*PipelinePack, numLines) ith.PackSupply = make(chan *PipelinePack, numLines) for i := 0; i < numLines; i++ { packs[i] = NewPipelinePack(ith.PackSupply) ith.PackSupply <- packs[i] } c.Specify("reads a log file", func() { // Expect InputRunner calls to get InChan and inject outgoing msgs ith.MockInputRunner.EXPECT().LogError(gomock.Any()).AnyTimes() ith.MockInputRunner.EXPECT().LogMessage(gomock.Any()).AnyTimes() ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply).Times(numLines) // Expect calls to get decoder and decode each message. Since the // decoding is a no-op, the message payload will be the log file // line, unchanged. pbcall := ith.MockHelper.EXPECT().DecoderRunner(lsiConfig.Decoder, "-"+lsiConfig.Decoder) pbcall.Return(mockDecoderRunner, true) decodeCall := mockDecoderRunner.EXPECT().InChan().Times(numLines) decodeCall.Return(ith.DecodeChan) runOutChan := make(chan error, 1) go func() { err = lsInput.Run(ith.MockInputRunner, ith.MockHelper) runOutChan <- err }() d, _ := time.ParseDuration("5s") timeout := time.After(d) timed := false for x := 0; x < numLines; x++ { select { case <-ith.DecodeChan: case <-timeout: timed = true x += numLines } // Free up the scheduler while we wait for the log file lines // to be processed. runtime.Gosched() } lsInput.Stop() c.Expect(timed, gs.Equals, false) c.Expect(<-runOutChan, gs.Equals, nil) }) }) c.Specify("with a translation map", func() { lsiConfig.Translation = make(ls.SubmatchTranslationMap) lsiConfig.Translation["Seq"] = make(ls.MatchTranslationMap) c.Specify("allows len 1 translation map for 'missing'", func() { lsiConfig.Translation["Seq"]["missing"] = 9999 err := lsInput.Init(lsiConfig) c.Expect(err, gs.IsNil) }) c.Specify("doesn't allow len 1 map for other keys", func() { lsiConfig.Translation["Seq"]["missin"] = 9999 err := lsInput.Init(lsiConfig) c.Expect(err, gs.Not(gs.IsNil)) c.Expect(err.Error(), gs.Equals, "A translation map with one entry ('Seq') must be specifying a "+ "'missing' key.") }) }) }) }
func NewOutputTestHelper(ctrl *gomock.Controller) (oth *OutputTestHelper) { oth = new(OutputTestHelper) oth.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) oth.MockOutputRunner = pipelinemock.NewMockOutputRunner(ctrl) return }
func NewFilterTestHelper(ctrl *gomock.Controller) (fth *FilterTestHelper) { fth = new(FilterTestHelper) fth.MockHelper = pm.NewMockPluginHelper(ctrl) fth.MockFilterRunner = pm.NewMockFilterRunner(ctrl) return }
func FileMonitorSpec(c gs.Context) { config := NewPipelineConfig(nil) tmpDir, tmpErr := ioutil.TempDir("", "hekad-tests-") c.Expect(tmpErr, gs.Equals, nil) origBaseDir := Globals().BaseDir Globals().BaseDir = tmpDir defer func() { Globals().BaseDir = origBaseDir tmpErr = os.RemoveAll(tmpDir) c.Expect(tmpErr, gs.Equals, nil) }() journalName := "test-seekjournal" journalDir := filepath.Join(tmpDir, "seekjournals") tmpErr = os.MkdirAll(journalDir, 0770) c.Expect(tmpErr, gs.Equals, nil) journalPath := filepath.Join(journalDir, journalName) t := &pipeline_ts.SimpleT{} ctrl := gomock.NewController(t) defer ctrl.Finish() ith := new(plugins_ts.InputTestHelper) ith.Msg = pipeline_ts.GetTestMessage() ith.Pack = NewPipelinePack(config.InputRecycleChan()) // Specify localhost, but we're not really going to use the network ith.AddrStr = "localhost:55565" ith.ResolvedAddrStr = "127.0.0.1:55565" // set up mock helper, decoder set, and packSupply channel ith.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) ith.MockInputRunner = pipelinemock.NewMockInputRunner(ctrl) ith.Decoder = pipelinemock.NewMockDecoderRunner(ctrl) ith.PackSupply = make(chan *PipelinePack, 1) ith.DecodeChan = make(chan *PipelinePack) c.Specify("saved last read position", func() { c.Specify("without a previous journal", func() { ith.MockInputRunner.EXPECT().LogError(gomock.Any()).AnyTimes() lfInput, lfiConfig := createLogfileInput(journalName) // Initialize the input test helper err := lfInput.Init(lfiConfig) c.Expect(err, gs.IsNil) dName := "decoder-name" lfInput.decoderName = dName mockDecoderRunner := pipelinemock.NewMockDecoderRunner(ctrl) // Create pool of packs. numLines := 95 // # of lines in the log file we're parsing. packs := make([]*PipelinePack, numLines) ith.PackSupply = make(chan *PipelinePack, numLines) for i := 0; i < numLines; i++ { packs[i] = NewPipelinePack(ith.PackSupply) ith.PackSupply <- packs[i] } // Expect InputRunner calls to get InChan and inject outgoing msgs ith.MockInputRunner.EXPECT().InChan().Return(ith.PackSupply).Times(numLines) // Expect calls to get decoder and decode each message. Since the // decoding is a no-op, the message payload will be the log file // line, unchanged. ith.MockInputRunner.EXPECT().Name().Return("FileMonitor") ith.MockHelper.EXPECT().DecoderRunner(dName, "FileMonitor-"+dName).Return(mockDecoderRunner, true) decodeCall := mockDecoderRunner.EXPECT().InChan().Times(numLines) decodeCall.Return(ith.DecodeChan) go func() { err = lfInput.Run(ith.MockInputRunner, ith.MockHelper) c.Expect(err, gs.IsNil) }() for x := 0; x < numLines; x++ { _ = <-ith.DecodeChan // Free up the scheduler while we wait for the log file lines // to be processed. runtime.Gosched() } newFM := new(FileMonitor) newFM.Init(lfiConfig) c.Expect(err, gs.Equals, nil) fbytes, _ := json.Marshal(lfInput.Monitor) json.Unmarshal(fbytes, &newFM) c.Expect(newFM.seek, gs.Equals, int64(28950)) lfInput.Stop() }) c.Specify("with a previous journal initializes with a seek value", func() { lfInput, lfiConfig := createLogfileInput(journalName) journalData := `{"last_hash":"f0b60af7f2cb35c3724151422e2f999af6e21fc0","last_start":28650,"last_len":300,"seek":28950}` journal, journalErr := os.OpenFile(journalPath, os.O_CREATE|os.O_RDWR, 0660) c.Expect(journalErr, gs.Equals, nil) journal.WriteString(journalData) journal.Close() err := lfInput.Init(lfiConfig) c.Expect(err, gs.IsNil) c.Expect(err, gs.IsNil) // # bytes should be set to what's in the journal data c.Expect(lfInput.Monitor.seek, gs.Equals, int64(28950)) }) c.Specify("resets last read position to 0 if hash doesn't match", func() { lfInput, lfiConfig := createLogfileInput(journalName) lfiConfig.ResumeFromStart = true journalData := `{"last_hash":"xxxxx","last_start":28650,"last_len":300,"seek":28950}` journal, journalErr := os.OpenFile(journalPath, os.O_CREATE|os.O_RDWR, 0660) c.Expect(journalErr, gs.Equals, nil) journal.WriteString(journalData) journal.Close() err := lfInput.Init(lfiConfig) c.Expect(err, gs.IsNil) // # bytes should be set to what's in the journal data c.Expect(lfInput.Monitor.seek, gs.Equals, int64(0)) }) c.Specify("resets last read position to end of file if hash doesn't match", func() { lfInput, lfiConfig := createLogfileInput(journalName) lfiConfig.ResumeFromStart = false journalData := `{"last_hash":"xxxxx","last_start":28650,"last_len":300,"seek":28950}` journal, journalErr := os.OpenFile(journalPath, os.O_CREATE|os.O_RDWR, 0660) c.Expect(journalErr, gs.Equals, nil) journal.WriteString(journalData) journal.Close() err := lfInput.Init(lfiConfig) c.Expect(err, gs.IsNil) // # bytes should be set to what's in the journal data c.Expect(lfInput.Monitor.seek, gs.Equals, int64(28950)) }) }) c.Specify("filemonitor generates expected journal path", func() { lfInput := new(LogfileInput) lfiConfig := lfInput.ConfigStruct().(*LogfileInputConfig) lfiConfig.LogFile = filepath.Join("..", "testsupport", "test-zeus.log") lfiConfig.DiscoverInterval = 5 lfiConfig.StatInterval = 5 err := lfInput.Init(lfiConfig) c.Expect(err, gs.Equals, nil) clean := filepath.Join(journalDir, "___testsupport_test-zeus_log") c.Expect(lfInput.Monitor.seekJournalPath, gs.Equals, clean) }) }