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()") }
// 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 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 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 NewOutputTestHelper(ctrl *gomock.Controller) (oth *OutputTestHelper) { oth = new(OutputTestHelper) oth.MockHelper = pipelinemock.NewMockPluginHelper(ctrl) oth.MockOutputRunner = pipelinemock.NewMockOutputRunner(ctrl) return }
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) }) }) } }) }