mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2026-05-17 00:26:36 +03:00
apptest/tests: only flush component logs on test failure (#9491)
Update tests to only print component output in integration tests if the test case is failing. An example of pipeline failure: https://github.com/VictoriaMetrics/VictoriaMetrics/actions/runs/16473633711/job/46569913168?pr=9491 --------- Signed-off-by: Zakhar Bessarab <z.bessarab@victoriametrics.com>
This commit is contained in:
@@ -32,6 +32,7 @@ type app struct {
|
||||
flags []string
|
||||
process *os.Process
|
||||
wait bool
|
||||
output io.Writer
|
||||
}
|
||||
|
||||
// appOptions holds the optional configuration of an app, such as default flags
|
||||
@@ -40,6 +41,7 @@ type appOptions struct {
|
||||
defaultFlags map[string]string
|
||||
extractREs []*regexp.Regexp
|
||||
wait bool
|
||||
output io.Writer
|
||||
}
|
||||
|
||||
// startApp starts an instance of an app using the app binary file path and
|
||||
@@ -70,15 +72,21 @@ func startApp(instance string, binary string, flags []string, opts *appOptions)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
output := opts.output
|
||||
if output == nil {
|
||||
output = os.Stderr
|
||||
}
|
||||
|
||||
app := &app{
|
||||
instance: instance,
|
||||
binary: binary,
|
||||
flags: flags,
|
||||
process: cmd.Process,
|
||||
wait: opts.wait,
|
||||
output: output,
|
||||
}
|
||||
|
||||
go app.processOutput("stdout", stdout, app.writeToStderr)
|
||||
go app.processOutput("stdout", stdout, app.writeToOutput)
|
||||
|
||||
lineProcessors := make([]lineProcessor, len(opts.extractREs))
|
||||
reExtractors := make([]*reExtractor, len(opts.extractREs))
|
||||
@@ -87,7 +95,7 @@ func startApp(instance string, binary string, flags []string, opts *appOptions)
|
||||
reExtractors[i] = newREExtractor(re, timeout)
|
||||
lineProcessors[i] = reExtractors[i].extractRE
|
||||
}
|
||||
go app.processOutput("stderr", stderr, append(lineProcessors, app.writeToStderr)...)
|
||||
go app.processOutput("stderr", stderr, append(lineProcessors, app.writeToOutput)...)
|
||||
|
||||
extracts, err := extractREs(reExtractors, timeout)
|
||||
if err != nil {
|
||||
@@ -177,11 +185,11 @@ func (app *app) processOutput(outputName string, output io.Reader, lps ...linePr
|
||||
}
|
||||
}
|
||||
|
||||
// writeToStderr is a line processor that writes the line to the stderr.
|
||||
// writeToOutput is a lineProcessor that writes the line to the output.
|
||||
// The function always returns false to indicate its caller that each line must
|
||||
// be written to the stderr.
|
||||
func (app *app) writeToStderr(line string) bool {
|
||||
fmt.Fprintf(os.Stderr, "%s %s\n", app.instance, line)
|
||||
func (app *app) writeToOutput(line string) bool {
|
||||
fmt.Fprintf(app.output, "%s %s\n", app.instance, line)
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,11 @@ package apptest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -18,6 +21,7 @@ type TestCase struct {
|
||||
t *testing.T
|
||||
cli *Client
|
||||
|
||||
output *outputProcessor
|
||||
startedApps map[string]Stopper
|
||||
}
|
||||
|
||||
@@ -29,7 +33,15 @@ type Stopper interface {
|
||||
// NewTestCase creates a new test case.
|
||||
func NewTestCase(t *testing.T) *TestCase {
|
||||
t.Parallel()
|
||||
return &TestCase{t, NewClient(), make(map[string]Stopper)}
|
||||
tc := &TestCase{t, NewClient(), &outputProcessor{make([]byte, 0), sync.Mutex{}}, make(map[string]Stopper)}
|
||||
|
||||
tc.t.Cleanup(func() {
|
||||
if tc.t.Failed() || testing.Verbose() {
|
||||
tc.output.FlushOutput()
|
||||
}
|
||||
})
|
||||
|
||||
return tc
|
||||
}
|
||||
|
||||
// T returns the test state.
|
||||
@@ -87,7 +99,7 @@ func (tc *TestCase) MustStartVmsingle(instance string, flags []string) *Vmsingle
|
||||
func (tc *TestCase) MustStartVmsingleAt(instance, binary string, flags []string) *Vmsingle {
|
||||
tc.t.Helper()
|
||||
|
||||
app, err := StartVmsingleAt(instance, binary, flags, tc.cli)
|
||||
app, err := StartVmsingleAt(instance, binary, flags, tc.cli, tc.output)
|
||||
if err != nil {
|
||||
tc.t.Fatalf("Could not start %s: %v", instance, err)
|
||||
}
|
||||
@@ -108,7 +120,7 @@ func (tc *TestCase) MustStartVmstorage(instance string, flags []string) *Vmstora
|
||||
func (tc *TestCase) MustStartVmstorageAt(instance string, binary string, flags []string) *Vmstorage {
|
||||
tc.t.Helper()
|
||||
|
||||
app, err := StartVmstorageAt(instance, binary, flags, tc.cli)
|
||||
app, err := StartVmstorageAt(instance, binary, flags, tc.cli, tc.output)
|
||||
if err != nil {
|
||||
tc.t.Fatalf("Could not start %s: %v", instance, err)
|
||||
}
|
||||
@@ -121,7 +133,7 @@ func (tc *TestCase) MustStartVmstorageAt(instance string, binary string, flags [
|
||||
func (tc *TestCase) MustStartVmselect(instance string, flags []string) *Vmselect {
|
||||
tc.t.Helper()
|
||||
|
||||
app, err := StartVmselect(instance, flags, tc.cli)
|
||||
app, err := StartVmselect(instance, flags, tc.cli, tc.output)
|
||||
if err != nil {
|
||||
tc.t.Fatalf("Could not start %s: %v", instance, err)
|
||||
}
|
||||
@@ -134,7 +146,7 @@ func (tc *TestCase) MustStartVmselect(instance string, flags []string) *Vmselect
|
||||
func (tc *TestCase) MustStartVminsert(instance string, flags []string) *Vminsert {
|
||||
tc.t.Helper()
|
||||
|
||||
app, err := StartVminsert(instance, flags, tc.cli)
|
||||
app, err := StartVminsert(instance, flags, tc.cli, tc.output)
|
||||
if err != nil {
|
||||
tc.t.Fatalf("Could not start %s: %v", instance, err)
|
||||
}
|
||||
@@ -149,7 +161,7 @@ func (tc *TestCase) MustStartVmagent(instance string, flags []string, promScrape
|
||||
|
||||
promScrapeConfigFilePath := path.Join(tc.t.TempDir(), "prometheus.yml")
|
||||
fs.MustWriteSync(promScrapeConfigFilePath, []byte(promScrapeConfigFileYAML))
|
||||
app, err := StartVmagent(instance, flags, tc.cli, promScrapeConfigFilePath)
|
||||
app, err := StartVmagent(instance, flags, tc.cli, promScrapeConfigFilePath, tc.output)
|
||||
if err != nil {
|
||||
tc.t.Fatalf("Could not start %s: %v", instance, err)
|
||||
}
|
||||
@@ -193,7 +205,7 @@ func (tc *TestCase) MustStartVmauth(instance string, flags []string, configFileY
|
||||
|
||||
configFilePath := path.Join(tc.t.TempDir(), "config.yaml")
|
||||
fs.MustWriteSync(configFilePath, []byte(configFileYAML))
|
||||
app, err := StartVmauth(instance, flags, tc.cli, configFilePath)
|
||||
app, err := StartVmauth(instance, flags, tc.cli, configFilePath, tc.output)
|
||||
if err != nil {
|
||||
tc.t.Fatalf("Could not start %s: %v", instance, err)
|
||||
}
|
||||
@@ -207,7 +219,7 @@ func (tc *TestCase) MustStartVmauth(instance string, flags []string, configFileY
|
||||
func (tc *TestCase) MustStartVmbackup(instance, storageDataPath, snapshotCreateURL, dst string) {
|
||||
tc.t.Helper()
|
||||
|
||||
if err := StartVmbackup(instance, storageDataPath, snapshotCreateURL, dst); err != nil {
|
||||
if err := StartVmbackup(instance, storageDataPath, snapshotCreateURL, dst, tc.output); err != nil {
|
||||
tc.t.Fatalf("vmbackup %q failed to start or exited with non-zero code: %v", instance, err)
|
||||
}
|
||||
|
||||
@@ -222,7 +234,7 @@ func (tc *TestCase) MustStartVmbackup(instance, storageDataPath, snapshotCreateU
|
||||
func (tc *TestCase) MustStartVmrestore(instance, src, storageDataPath string) {
|
||||
tc.t.Helper()
|
||||
|
||||
if err := StartVmrestore(instance, src, storageDataPath); err != nil {
|
||||
if err := StartVmrestore(instance, src, storageDataPath, tc.output); err != nil {
|
||||
tc.t.Fatalf("vmrestore %q failed to start or exited with non-zero code: %v", instance, err)
|
||||
}
|
||||
|
||||
@@ -307,7 +319,7 @@ func (tc *TestCase) MustStartCluster(opts *ClusterOptions) *Vmcluster {
|
||||
func (tc *TestCase) MustStartVmctl(instance string, flags []string) {
|
||||
tc.t.Helper()
|
||||
|
||||
err := StartVmctl(instance, flags)
|
||||
err := StartVmctl(instance, flags, tc.output)
|
||||
if err != nil {
|
||||
tc.t.Fatalf("Could not start %s: %v", instance, err)
|
||||
}
|
||||
@@ -429,3 +441,25 @@ func (tc *TestCase) Assert(opts *AssertOptions) {
|
||||
tc.t.Error(msg)
|
||||
}
|
||||
}
|
||||
|
||||
var _ io.Writer = &outputProcessor{}
|
||||
|
||||
type outputProcessor struct {
|
||||
entries []byte
|
||||
entriesLock sync.Mutex
|
||||
}
|
||||
|
||||
func (op *outputProcessor) Write(p []byte) (n int, err error) {
|
||||
op.entriesLock.Lock()
|
||||
defer op.entriesLock.Unlock()
|
||||
op.entries = append(op.entries, p...)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (op *outputProcessor) FlushOutput() {
|
||||
op.entriesLock.Lock()
|
||||
defer op.entriesLock.Unlock()
|
||||
|
||||
os.Stderr.Write(op.entries)
|
||||
op.entries = nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package apptest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
@@ -23,7 +24,7 @@ type Vmagent struct {
|
||||
// StartVmagent starts an instance of vmagent with the given flags. It also
|
||||
// sets the default flags and populates the app instance state with runtime
|
||||
// values extracted from the application log (such as httpListenAddr)
|
||||
func StartVmagent(instance string, flags []string, cli *Client, promScrapeConfigFilePath string) (*Vmagent, error) {
|
||||
func StartVmagent(instance string, flags []string, cli *Client, promScrapeConfigFilePath string, output io.Writer) (*Vmagent, error) {
|
||||
extractREs := []*regexp.Regexp{
|
||||
httpListenAddrRE,
|
||||
}
|
||||
@@ -35,6 +36,7 @@ func StartVmagent(instance string, flags []string, cli *Client, promScrapeConfig
|
||||
"-remoteWrite.tmpDataPath": fmt.Sprintf("%s/%s-%d", os.TempDir(), instance, time.Now().UnixNano()),
|
||||
},
|
||||
extractREs: extractREs,
|
||||
output: output,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -2,6 +2,7 @@ package apptest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"syscall"
|
||||
"testing"
|
||||
@@ -26,7 +27,7 @@ type Vmauth struct {
|
||||
// StartVmauth starts an instance of vmauth with the given flags. It also
|
||||
// sets the default flags and populates the app instance state with runtime
|
||||
// values extracted from the application log (such as httpListenAddr)
|
||||
func StartVmauth(instance string, flags []string, cli *Client, configFilePath string) (*Vmauth, error) {
|
||||
func StartVmauth(instance string, flags []string, cli *Client, configFilePath string, output io.Writer) (*Vmauth, error) {
|
||||
extractREs := []*regexp.Regexp{
|
||||
httpBuilitinListenAddrRE,
|
||||
}
|
||||
@@ -37,6 +38,7 @@ func StartVmauth(instance string, flags []string, cli *Client, configFilePath st
|
||||
"-auth.config": configFilePath,
|
||||
},
|
||||
extractREs: extractREs,
|
||||
output: output,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
package apptest
|
||||
|
||||
import "io"
|
||||
|
||||
// StartVmbackup starts an instance of vmbackup with the given flags and waits
|
||||
// until it exits.
|
||||
func StartVmbackup(instance, storageDataPath, snapshotCreateURL, dst string) error {
|
||||
func StartVmbackup(instance, storageDataPath, snapshotCreateURL, dst string, output io.Writer) error {
|
||||
flags := []string{
|
||||
"-storageDataPath=" + storageDataPath,
|
||||
"-snapshot.createURL=" + snapshotCreateURL,
|
||||
"-dst=" + dst,
|
||||
}
|
||||
_, _, err := startApp(instance, "../../bin/vmbackup", flags, &appOptions{wait: true})
|
||||
_, _, err := startApp(instance, "../../bin/vmbackup", flags, &appOptions{wait: true, output: output})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package apptest
|
||||
|
||||
import "io"
|
||||
|
||||
// StartVmctl starts an instance of vmctl cli with the given flags
|
||||
func StartVmctl(instance string, flags []string) error {
|
||||
_, _, err := startApp(instance, "../../bin/vmctl", flags, &appOptions{wait: true})
|
||||
func StartVmctl(instance string, flags []string, output io.Writer) error {
|
||||
_, _, err := startApp(instance, "../../bin/vmctl", flags, &appOptions{wait: true, output: output})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package apptest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -41,7 +42,7 @@ func storageNodes(flags []string) []string {
|
||||
// StartVminsert starts an instance of vminsert with the given flags. It also
|
||||
// sets the default flags and populates the app instance state with runtime
|
||||
// values extracted from the application log (such as httpListenAddr)
|
||||
func StartVminsert(instance string, flags []string, cli *Client) (*Vminsert, error) {
|
||||
func StartVminsert(instance string, flags []string, cli *Client, output io.Writer) (*Vminsert, error) {
|
||||
extractREs := []*regexp.Regexp{
|
||||
httpListenAddrRE,
|
||||
vminsertClusterNativeAddrRE,
|
||||
@@ -63,6 +64,7 @@ func StartVminsert(instance string, flags []string, cli *Client) (*Vminsert, err
|
||||
"-opentsdbListenAddr": "127.0.0.1:0",
|
||||
},
|
||||
extractREs: extractREs,
|
||||
output: output,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package apptest
|
||||
|
||||
import "io"
|
||||
|
||||
// StartVmrestore starts an instance of vmrestore with the given flags and waits
|
||||
// until it exits.
|
||||
func StartVmrestore(instance, src, storageDataPath string) error {
|
||||
func StartVmrestore(instance, src, storageDataPath string, output io.Writer) error {
|
||||
flags := []string{
|
||||
"-src=" + src,
|
||||
"-storageDataPath=" + storageDataPath,
|
||||
}
|
||||
_, _, err := startApp(instance, "../../bin/vmrestore", flags, &appOptions{wait: true})
|
||||
_, _, err := startApp(instance, "../../bin/vmrestore", flags, &appOptions{wait: true, output: output})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package apptest
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"testing"
|
||||
@@ -22,7 +23,7 @@ type Vmselect struct {
|
||||
// StartVmselect starts an instance of vmselect with the given flags. It also
|
||||
// sets the default flags and populates the app instance state with runtime
|
||||
// values extracted from the application log (such as httpListenAddr)
|
||||
func StartVmselect(instance string, flags []string, cli *Client) (*Vmselect, error) {
|
||||
func StartVmselect(instance string, flags []string, cli *Client, output io.Writer) (*Vmselect, error) {
|
||||
app, stderrExtracts, err := startApp(instance, "../../bin/vmselect", flags, &appOptions{
|
||||
defaultFlags: map[string]string{
|
||||
"-httpListenAddr": "127.0.0.1:0",
|
||||
@@ -32,6 +33,7 @@ func StartVmselect(instance string, flags []string, cli *Client) (*Vmselect, err
|
||||
httpListenAddrRE,
|
||||
vmselectAddrRE,
|
||||
},
|
||||
output: output,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -3,6 +3,7 @@ package apptest
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
@@ -46,7 +47,7 @@ type Vmsingle struct {
|
||||
// StartVmsingleAt starts an instance of vmsingle with the given flags. It also
|
||||
// sets the default flags and populates the app instance state with runtime
|
||||
// values extracted from the application log (such as httpListenAddr).
|
||||
func StartVmsingleAt(instance, binary string, flags []string, cli *Client) (*Vmsingle, error) {
|
||||
func StartVmsingleAt(instance, binary string, flags []string, cli *Client, output io.Writer) (*Vmsingle, error) {
|
||||
app, stderrExtracts, err := startApp(instance, binary, flags, &appOptions{
|
||||
defaultFlags: map[string]string{
|
||||
"-storageDataPath": fmt.Sprintf("%s/%s-%d", os.TempDir(), instance, time.Now().UnixNano()),
|
||||
@@ -60,6 +61,7 @@ func StartVmsingleAt(instance, binary string, flags []string, cli *Client) (*Vms
|
||||
graphiteListenAddrRE,
|
||||
openTSDBListenAddrRE,
|
||||
},
|
||||
output: output,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -3,6 +3,7 @@ package apptest
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
@@ -25,7 +26,7 @@ type Vmstorage struct {
|
||||
// StartVmstorageAt starts an instance of vmstorage with the given flags. It also
|
||||
// sets the default flags and populates the app instance state with runtime
|
||||
// values extracted from the application log (such as httpListenAddr)
|
||||
func StartVmstorageAt(instance, binary string, flags []string, cli *Client) (*Vmstorage, error) {
|
||||
func StartVmstorageAt(instance, binary string, flags []string, cli *Client, output io.Writer) (*Vmstorage, error) {
|
||||
app, stderrExtracts, err := startApp(instance, binary, flags, &appOptions{
|
||||
defaultFlags: map[string]string{
|
||||
"-storageDataPath": fmt.Sprintf("%s/%s-%d", os.TempDir(), instance, time.Now().UnixNano()),
|
||||
@@ -39,6 +40,7 @@ func StartVmstorageAt(instance, binary string, flags []string, cli *Client) (*Vm
|
||||
vminsertAddrRE,
|
||||
vmselectAddrRE,
|
||||
},
|
||||
output: output,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in New Issue
Block a user