github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/clients/pkg/promtail/targets/file/filetargetmanager_test.go (about)

     1  package file
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/go-kit/log"
    14  	"github.com/prometheus/common/model"
    15  	"github.com/prometheus/prometheus/discovery"
    16  	"github.com/prometheus/prometheus/discovery/targetgroup"
    17  	"gopkg.in/fsnotify.v1"
    18  
    19  	"github.com/grafana/loki/clients/pkg/promtail/api"
    20  	"github.com/grafana/loki/clients/pkg/promtail/client/fake"
    21  	"github.com/grafana/loki/clients/pkg/promtail/positions"
    22  	"github.com/grafana/loki/clients/pkg/promtail/scrapeconfig"
    23  )
    24  
    25  func newTestLogDirectories(t *testing.T) string {
    26  	tmpDir := t.TempDir()
    27  	logFileDir := filepath.Join(tmpDir, "logs")
    28  	err := os.MkdirAll(logFileDir, 0750)
    29  	assert.NoError(t, err)
    30  	return logFileDir
    31  }
    32  
    33  func newTestPositions(logger log.Logger, filePath string) (positions.Positions, error) {
    34  	// Set the sync period to a really long value, to guarantee the sync timer never runs, this way we know
    35  	// everything saved was done through channel notifications when target.stop() was called.
    36  	pos, err := positions.New(logger, positions.Config{
    37  		SyncPeriod:    60 * time.Second,
    38  		PositionsFile: filePath,
    39  	})
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	return pos, nil
    44  }
    45  
    46  func newTestFileTargetManager(logger log.Logger, client api.EntryHandler, positions positions.Positions, observePath string) (*FileTargetManager, error) {
    47  	targetGroup := targetgroup.Group{
    48  		Targets: []model.LabelSet{{
    49  			"localhost": "",
    50  		}},
    51  		Labels: model.LabelSet{
    52  			"job":      "varlogs",
    53  			"match":    "true",
    54  			"__path__": model.LabelValue(observePath),
    55  		},
    56  		Source: "",
    57  	}
    58  	sc := scrapeconfig.Config{
    59  		JobName:        "",
    60  		PipelineStages: nil,
    61  		RelabelConfigs: nil,
    62  		ServiceDiscoveryConfig: scrapeconfig.ServiceDiscoveryConfig{
    63  			StaticConfigs: discovery.StaticConfig{
    64  				&targetGroup,
    65  			},
    66  		},
    67  	}
    68  	tc := &Config{
    69  		SyncPeriod: 1 * time.Second,
    70  	}
    71  
    72  	metrics := NewMetrics(nil)
    73  	ftm, err := NewFileTargetManager(metrics, logger, positions, client, []scrapeconfig.Config{sc}, tc)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	return ftm, nil
    78  }
    79  
    80  func TestLongPositionsSyncDelayStillSavesCorrectPosition(t *testing.T) {
    81  	w := log.NewSyncWriter(os.Stderr)
    82  	logger := log.NewLogfmtLogger(w)
    83  	logDirName := newTestLogDirectories(t)
    84  
    85  	logFile := filepath.Join(logDirName, "test.log")
    86  	positionsFileName := filepath.Join(logDirName, "positions.yml")
    87  	ps, err := newTestPositions(logger, positionsFileName)
    88  	if err != nil {
    89  		t.Fatal(err)
    90  	}
    91  
    92  	client := fake.New(func() {})
    93  	defer client.Stop()
    94  
    95  	ftm, err := newTestFileTargetManager(logger, client, ps, logDirName+"/*")
    96  	if err != nil {
    97  		t.Fatal(err)
    98  	}
    99  
   100  	f, err := os.Create(logFile)
   101  	if err != nil {
   102  		t.Fatal(err)
   103  	}
   104  
   105  	for i := 0; i < 10; i++ {
   106  		_, err = f.WriteString("test\n")
   107  		if err != nil {
   108  			t.Fatal(err)
   109  		}
   110  		time.Sleep(1 * time.Millisecond)
   111  	}
   112  
   113  	assert.Eventually(t, func() bool {
   114  		return len(client.Received()) == 10
   115  	}, time.Second*10, time.Millisecond*1)
   116  
   117  	ftm.Stop()
   118  	ps.Stop()
   119  
   120  	// Assert the position value is in the correct spot.
   121  	val, err := ps.Get(logFile)
   122  	if err != nil {
   123  		t.Errorf("Positions file did not contain any data for our test log file, err: %s", err.Error())
   124  	}
   125  	if val != 50 {
   126  		t.Error("Incorrect position found, expected 50, found", val)
   127  	}
   128  
   129  	// Assert the number of messages the handler received is correct.
   130  	if len(client.Received()) != 10 {
   131  		t.Error("Handler did not receive the correct number of messages, expected 10 received", len(client.Received()))
   132  	}
   133  
   134  	// Spot check one of the messages.
   135  	if client.Received()[0].Line != "test" {
   136  		t.Error("Expected first log message to be 'test' but was", client.Received()[0])
   137  	}
   138  }
   139  
   140  func TestWatchEntireDirectory(t *testing.T) {
   141  	w := log.NewSyncWriter(os.Stderr)
   142  	logger := log.NewLogfmtLogger(w)
   143  	logDirName := newTestLogDirectories(t)
   144  
   145  	logFile := filepath.Join(logDirName, "test.log")
   146  	positionsFileName := filepath.Join(logDirName, "positions.yml")
   147  	ps, err := newTestPositions(logger, positionsFileName)
   148  	if err != nil {
   149  		t.Fatal(err)
   150  	}
   151  
   152  	client := fake.New(func() {})
   153  	defer client.Stop()
   154  
   155  	ftm, err := newTestFileTargetManager(logger, client, ps, logDirName+"/*")
   156  	if err != nil {
   157  		t.Fatal(err)
   158  	}
   159  
   160  	f, err := os.Create(logFile)
   161  	if err != nil {
   162  		t.Fatal(err)
   163  	}
   164  
   165  	for i := 0; i < 10; i++ {
   166  		_, err = f.WriteString("test\n")
   167  		if err != nil {
   168  			t.Fatal(err)
   169  		}
   170  		time.Sleep(1 * time.Millisecond)
   171  	}
   172  
   173  	assert.Eventually(t, func() bool {
   174  		return len(client.Received()) == 10
   175  	}, time.Second*10, time.Millisecond*1)
   176  
   177  	ftm.Stop()
   178  	ps.Stop()
   179  
   180  	// Assert the position value is in the correct spot.
   181  	val, err := ps.Get(logFile)
   182  	if err != nil {
   183  		t.Errorf("Positions file did not contain any data for our test log file, err: %s", err.Error())
   184  	}
   185  	if val != 50 {
   186  		t.Error("Incorrect position found, expected 50, found", val)
   187  	}
   188  
   189  	// Assert the number of messages the handler received is correct.
   190  	if len(client.Received()) != 10 {
   191  		t.Error("Handler did not receive the correct number of messages, expected 10 received", len(client.Received()))
   192  	}
   193  
   194  	// Spot check one of the messages.
   195  	if client.Received()[0].Line != "test" {
   196  		t.Error("Expected first log message to be 'test' but was", client.Received()[0].Line)
   197  	}
   198  }
   199  
   200  func TestFileRolls(t *testing.T) {
   201  	w := log.NewSyncWriter(os.Stderr)
   202  	logger := log.NewLogfmtLogger(w)
   203  	logDirName := newTestLogDirectories(t)
   204  
   205  	logFile := filepath.Join(logDirName, "test.log")
   206  	positionsFileName := filepath.Join(logDirName, "positions.yml")
   207  	ps, err := newTestPositions(logger, positionsFileName)
   208  	if err != nil {
   209  		t.Fatal(err)
   210  	}
   211  
   212  	client := fake.New(func() {})
   213  	defer client.Stop()
   214  
   215  	ftm, err := newTestFileTargetManager(logger, client, ps, logDirName+"/*.log")
   216  	if err != nil {
   217  		t.Fatal(err)
   218  	}
   219  	f, err := os.Create(logFile)
   220  	if err != nil {
   221  		t.Fatal(err)
   222  	}
   223  
   224  	for i := 0; i < 10; i++ {
   225  		_, err = f.WriteString("test1\n")
   226  		if err != nil {
   227  			t.Fatal(err)
   228  		}
   229  		time.Sleep(1 * time.Millisecond)
   230  	}
   231  	assert.Eventually(t, func() bool {
   232  		return len(client.Received()) == 10
   233  	}, time.Second*10, time.Millisecond*1)
   234  
   235  	// Rename the log file to something not in the pattern, then create a new file with the same name.
   236  	err = os.Rename(logFile, filepath.Join(logDirName, "test.log.1"))
   237  	if err != nil {
   238  		t.Fatal("Failed to rename log file for test", err)
   239  	}
   240  	f, err = os.Create(logFile)
   241  	if err != nil {
   242  		t.Fatal(err)
   243  	}
   244  	for i := 0; i < 10; i++ {
   245  		_, err = f.WriteString("test2\n")
   246  		if err != nil {
   247  			t.Fatal(err)
   248  		}
   249  		time.Sleep(1 * time.Millisecond)
   250  	}
   251  	assert.Eventually(t, func() bool {
   252  		return len(client.Received()) == 20
   253  	}, time.Second*10, time.Millisecond*1)
   254  
   255  	ftm.Stop()
   256  	ps.Stop()
   257  
   258  	if len(client.Received()) != 20 {
   259  		t.Error("Handler did not receive the correct number of messages, expected 20 received", len(client.Received()))
   260  	}
   261  
   262  	// Spot check one of the messages.
   263  	if client.Received()[0].Line != "test1" {
   264  		t.Error("Expected first log message to be 'test1' but was", client.Received()[0].Line)
   265  	}
   266  
   267  	// Spot check the first message from the second file.
   268  	if client.Received()[10].Line != "test2" {
   269  		t.Error("Expected first log message to be 'test2' but was", client.Received()[10].Line)
   270  	}
   271  }
   272  
   273  func TestResumesWhereLeftOff(t *testing.T) {
   274  	w := log.NewSyncWriter(os.Stderr)
   275  	logger := log.NewLogfmtLogger(w)
   276  	logDirName := newTestLogDirectories(t)
   277  
   278  	logFile := filepath.Join(logDirName, "test.log")
   279  	positionsFileName := filepath.Join(logDirName, "positions.yml")
   280  	ps, err := newTestPositions(logger, positionsFileName)
   281  	if err != nil {
   282  		t.Fatal(err)
   283  	}
   284  
   285  	client := fake.New(func() {})
   286  	defer client.Stop()
   287  
   288  	ftm, err := newTestFileTargetManager(logger, client, ps, logDirName+"/*.log")
   289  	if err != nil {
   290  		t.Fatal(err)
   291  	}
   292  	f, err := os.Create(logFile)
   293  	if err != nil {
   294  		t.Fatal(err)
   295  	}
   296  
   297  	for i := 0; i < 10; i++ {
   298  		_, err = f.WriteString("test1\n")
   299  		if err != nil {
   300  			t.Fatal(err)
   301  		}
   302  		time.Sleep(1 * time.Millisecond)
   303  	}
   304  	assert.Eventually(t, func() bool {
   305  		return len(client.Received()) == 10
   306  	}, time.Second*10, time.Millisecond*1)
   307  
   308  	ftm.Stop()
   309  	ps.Stop()
   310  
   311  	// Create another positions (so that it loads from the previously saved positions file).
   312  	ps2, err := newTestPositions(logger, positionsFileName)
   313  	if err != nil {
   314  		t.Fatal(err)
   315  	}
   316  
   317  	// Create a new target manager, keep the same client so we can track what was sent through the handler.
   318  	ftm2, err := newTestFileTargetManager(logger, client, ps2, logDirName+"/*.log")
   319  	if err != nil {
   320  		t.Fatal(err)
   321  	}
   322  
   323  	for i := 0; i < 10; i++ {
   324  		_, err = f.WriteString("test2\n")
   325  		if err != nil {
   326  			t.Fatal(err)
   327  		}
   328  		time.Sleep(1 * time.Millisecond)
   329  	}
   330  	assert.Eventually(t, func() bool {
   331  		return len(client.Received()) == 20
   332  	}, time.Second*10, time.Millisecond*1)
   333  
   334  	ftm2.Stop()
   335  	ps2.Stop()
   336  
   337  	if len(client.Received()) != 20 {
   338  		t.Error("Handler did not receive the correct number of messages, expected 20 received", len(client.Received()))
   339  	}
   340  
   341  	// Spot check one of the messages.
   342  	if client.Received()[0].Line != "test1" {
   343  		t.Error("Expected first log message to be 'test1' but was", client.Received()[0])
   344  	}
   345  
   346  	// Spot check the first message from the second file.
   347  	if client.Received()[10].Line != "test2" {
   348  		t.Error("Expected first log message to be 'test2' but was", client.Received()[10])
   349  	}
   350  }
   351  
   352  func TestGlobWithMultipleFiles(t *testing.T) {
   353  	w := log.NewSyncWriter(os.Stderr)
   354  	logger := log.NewLogfmtLogger(w)
   355  	logDirName := newTestLogDirectories(t)
   356  
   357  	logFile1 := filepath.Join(logDirName, "test.log")
   358  	logFile2 := filepath.Join(logDirName, "dirt.log")
   359  	positionsFileName := filepath.Join(logDirName, "positions.yml")
   360  	ps, err := newTestPositions(logger, positionsFileName)
   361  	if err != nil {
   362  		t.Fatal(err)
   363  	}
   364  
   365  	client := fake.New(func() {})
   366  	defer client.Stop()
   367  
   368  	ftm, err := newTestFileTargetManager(logger, client, ps, logDirName+"/*.log")
   369  	if err != nil {
   370  		t.Fatal(err)
   371  	}
   372  
   373  	f1, err := os.Create(logFile1)
   374  	if err != nil {
   375  		t.Fatal(err)
   376  	}
   377  	f2, err := os.Create(logFile2)
   378  	if err != nil {
   379  		t.Fatal(err)
   380  	}
   381  
   382  	for i := 0; i < 10; i++ {
   383  		_, err = f1.WriteString("test1\n")
   384  		if err != nil {
   385  			t.Fatal(err)
   386  		}
   387  		time.Sleep(1 * time.Millisecond)
   388  		_, err = f2.WriteString("dirt1\n")
   389  		if err != nil {
   390  			t.Fatal(err)
   391  		}
   392  		time.Sleep(1 * time.Millisecond)
   393  	}
   394  	assert.Eventually(t, func() bool {
   395  		return len(client.Received()) == 20
   396  	}, time.Second*10, time.Millisecond*1)
   397  
   398  	ftm.Stop()
   399  	ps.Stop()
   400  
   401  	// Assert the position value is in the correct spot.
   402  	val, err := ps.Get(logFile1)
   403  	if err != nil {
   404  		t.Errorf("Positions file did not contain any data for our test log file, err: %s", err.Error())
   405  	}
   406  	if val != 60 {
   407  		t.Error("Incorrect position found for file 1, expected 60, found", val)
   408  	}
   409  	val, err = ps.Get(logFile2)
   410  	if err != nil {
   411  		t.Errorf("Positions file did not contain any data for our test log file, err: %s", err.Error())
   412  	}
   413  	if val != 60 {
   414  		t.Error("Incorrect position found for file 2, expected 60, found", val)
   415  	}
   416  
   417  	// Assert the number of messages the handler received is correct.
   418  	if len(client.Received()) != 20 {
   419  		t.Error("Handler did not receive the correct number of messages, expected 20 received", len(client.Received()))
   420  	}
   421  }
   422  
   423  func TestDeadlockTargetManager(t *testing.T) {
   424  	client := fake.New(func() {})
   425  	defer client.Stop()
   426  
   427  	targetEventHandler := make(chan fileTargetEvent)
   428  	defer func() {
   429  		close(targetEventHandler)
   430  	}()
   431  
   432  	syncer := &targetSyncer{
   433  		metrics:           NewMetrics(nil),
   434  		log:               log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)),
   435  		positions:         nil,
   436  		entryHandler:      client,
   437  		hostname:          "localhost",
   438  		fileEventWatchers: make(map[string]chan fsnotify.Event),
   439  		targets:           make(map[string]*FileTarget),
   440  		targetConfig: &Config{
   441  			SyncPeriod: time.Hour,
   442  		},
   443  	}
   444  
   445  	syncer.sync([]*targetgroup.Group{
   446  		{
   447  			Targets: []model.LabelSet{
   448  				{
   449  					hostLabel: "localhost",
   450  					pathLabel: "baz",
   451  					"job":     "bar",
   452  				},
   453  			},
   454  		},
   455  	}, targetEventHandler)
   456  
   457  	require.Equal(t, len(syncer.targets), 1)
   458  	require.Equal(t, len(syncer.fileEventWatchers), 1)
   459  
   460  	syncer.sync([]*targetgroup.Group{
   461  		{
   462  			Targets: []model.LabelSet{
   463  				{},
   464  			},
   465  		},
   466  	}, targetEventHandler)
   467  
   468  	syncer.sendFileCreateEvent(fsnotify.Event{Name: "baz"})
   469  
   470  	require.Equal(t, len(syncer.targets), 0)
   471  	require.Equal(t, len(syncer.fileEventWatchers), 0)
   472  }
   473  
   474  func TestDeadlockStartWatchingDuringSync(t *testing.T) {
   475  	w := log.NewSyncWriter(os.Stderr)
   476  	logger := log.NewLogfmtLogger(w)
   477  	oldLogDir := newTestLogDirectories(t)
   478  	newLogDir := newTestLogDirectories(t)
   479  
   480  	positionsFileName := filepath.Join(oldLogDir, "positions.yml")
   481  	ps, err := newTestPositions(logger, positionsFileName)
   482  	assert.NoError(t, err)
   483  
   484  	client := fake.New(func() {})
   485  	defer client.Stop()
   486  
   487  	ftm, err := newTestFileTargetManager(logger, client, ps, oldLogDir+"/*")
   488  	assert.NoError(t, err)
   489  
   490  	done := make(chan struct{})
   491  	go func() {
   492  		for i := 0; i < 10; i++ {
   493  			dir := filepath.Join(newLogDir, fmt.Sprintf("%d", i))
   494  			err := os.MkdirAll(dir, 0750)
   495  			assert.NoError(t, err)
   496  			time.Sleep(1 * time.Millisecond)
   497  			for j := 0; j < 100; j++ {
   498  				logFile := filepath.Join(dir, fmt.Sprintf("test%d.log", j))
   499  				f, err := os.Create(logFile)
   500  				assert.NoError(t, err)
   501  				_, err = f.WriteString("just a log line\n")
   502  				assert.NoError(t, err)
   503  				err = f.Close()
   504  				assert.NoError(t, err)
   505  				time.Sleep(1 * time.Millisecond)
   506  			}
   507  		}
   508  		close(done)
   509  	}()
   510  
   511  	tg := targetgroup.Group{
   512  		Targets: []model.LabelSet{{
   513  			"localhost": "",
   514  		}},
   515  		Labels: model.LabelSet{
   516  			"job":      "varlogs",
   517  			"match":    "true",
   518  			"__path__": model.LabelValue(newLogDir + "/**/*.log"),
   519  		},
   520  		Source: "",
   521  	}
   522  	for i := 0; i < 10; i++ {
   523  		ftm.syncers[""].sync([]*targetgroup.Group{&tg}, ftm.targetEventHandler)
   524  	}
   525  	<-done
   526  
   527  	ftm.Stop()
   528  	ps.Stop()
   529  }