Path: blob/main/component/common/loki/positions/positions_test.go
4096 views
package positions12// This code is copied from Promtail. The positions package allows logging3// components to keep track of read file offsets on disk and continue from the4// same place in case of a restart.56import (7"os"8"strings"9"testing"10"time"1112"github.com/go-kit/log"13"github.com/stretchr/testify/require"1415util_log "github.com/grafana/loki/pkg/util/log"16)1718func tempFilename(t *testing.T) string {19t.Helper()2021temp, err := os.CreateTemp("", "positions")22if err != nil {23t.Fatal("tempFilename:", err)24}25err = temp.Close()26if err != nil {27t.Fatal("tempFilename:", err)28}2930name := temp.Name()31err = os.Remove(name)32if err != nil {33t.Fatal("tempFilename:", err)34}3536return name37}3839func TestReadPositionsOK(t *testing.T) {40temp := tempFilename(t)41defer func() {42_ = os.Remove(temp)43}()4445yaml := []byte(`46positions:47? path: /tmp/random.log48labels: '{job="tmp"}'49: "17623"50`)51err := os.WriteFile(temp, yaml, 0644)52if err != nil {53t.Fatal(err)54}5556pos, err := readPositionsFile(Config{57PositionsFile: temp,58}, log.NewNopLogger())5960require.NoError(t, err)61require.Equal(t, "17623", pos[Entry{62Path: "/tmp/random.log",63Labels: `{job="tmp"}`,64}])65}6667func TestReadPositionsEmptyFile(t *testing.T) {68temp := tempFilename(t)69defer func() {70_ = os.Remove(temp)71}()7273yaml := []byte(``)74err := os.WriteFile(temp, yaml, 0644)75if err != nil {76t.Fatal(err)77}7879pos, err := readPositionsFile(Config{80PositionsFile: temp,81}, log.NewNopLogger())8283require.NoError(t, err)84require.NotNil(t, pos)85}8687func TestReadPositionsFromDir(t *testing.T) {88temp := tempFilename(t)89err := os.Mkdir(temp, 0644)90if err != nil {91t.Fatal(err)92}9394defer func() {95_ = os.Remove(temp)96}()9798_, err = readPositionsFile(Config{99PositionsFile: temp,100}, log.NewNopLogger())101102require.Error(t, err)103require.True(t, strings.Contains(err.Error(), temp)) // error must contain filename104}105106func TestReadPositionsFromBadYaml(t *testing.T) {107temp := tempFilename(t)108defer func() {109_ = os.Remove(temp)110}()111112badYaml := []byte(`113positions:114? path: /tmp/random.log115labels: "{}"116: "176117`)118err := os.WriteFile(temp, badYaml, 0644)119if err != nil {120t.Fatal(err)121}122123_, err = readPositionsFile(Config{124PositionsFile: temp,125}, log.NewNopLogger())126127require.Error(t, err)128require.True(t, strings.Contains(err.Error(), temp)) // error must contain filename129}130131func TestReadPositionsFromBadYamlIgnoreCorruption(t *testing.T) {132temp := tempFilename(t)133defer func() {134_ = os.Remove(temp)135}()136137badYaml := []byte(`138positions:139? path: /tmp/random.log140labels: "{}"141: "176142`)143err := os.WriteFile(temp, badYaml, 0644)144if err != nil {145t.Fatal(err)146}147148out, err := readPositionsFile(Config{149PositionsFile: temp,150IgnoreInvalidYaml: true,151}, log.NewNopLogger())152153require.NoError(t, err)154require.Equal(t, map[Entry]string{}, out)155}156157func Test_ReadOnly(t *testing.T) {158temp := tempFilename(t)159defer func() {160_ = os.Remove(temp)161}()162yaml := []byte(`163positions:164? path: /tmp/random.log165labels: '{job="tmp"}'166: "17623"167`)168err := os.WriteFile(temp, yaml, 0644)169if err != nil {170t.Fatal(err)171}172p, err := New(util_log.Logger, Config{173SyncPeriod: 20 * time.Second,174PositionsFile: temp,175ReadOnly: true,176})177if err != nil {178t.Fatal(err)179}180defer p.Stop()181p.Put("/foo/bar/f", "", 12132132)182p.PutString("/foo/f", "", "100")183pos, err := p.Get("/tmp/random.log", `{job="tmp"}`)184if err != nil {185t.Fatal(err)186}187require.Equal(t, int64(17623), pos)188p.(*positions).save()189out, err := readPositionsFile(Config{190PositionsFile: temp,191IgnoreInvalidYaml: true,192ReadOnly: true,193}, log.NewNopLogger())194195require.NoError(t, err)196require.Equal(t, map[Entry]string{197{Path: "/tmp/random.log", Labels: `{job="tmp"}`}: "17623",198}, out)199}200201func TestWriteEmptyLabels(t *testing.T) {202temp := tempFilename(t)203defer func() {204_ = os.Remove(temp)205}()206yaml := []byte(`207positions:208? path: /tmp/initial.log209labels: '{job="tmp"}'210: "10030"211`)212err := os.WriteFile(temp, yaml, 0644)213if err != nil {214t.Fatal(err)215}216p, err := New(util_log.Logger, Config{217SyncPeriod: 20 * time.Second,218PositionsFile: temp,219})220if err != nil {221t.Fatal(err)222}223defer p.Stop()224p.Put("/tmp/foo/nolabels.log", "", 10040)225p.Put("/tmp/foo/emptylabels.log", "{}", 10050)226p.PutString("/tmp/bar/nolabels.log", "", "10060")227p.PutString("/tmp/bar/emptylabels.log", "{}", "10070")228pos, err := p.Get("/tmp/initial.log", `{job="tmp"}`)229if err != nil {230t.Fatal(err)231}232require.Equal(t, int64(10030), pos)233p.(*positions).save()234out, err := readPositionsFile(Config{235PositionsFile: temp,236IgnoreInvalidYaml: true,237ReadOnly: false,238}, log.NewNopLogger())239240require.NoError(t, err)241require.Equal(t, map[Entry]string{242{Path: "/tmp/initial.log", Labels: `{job="tmp"}`}: "10030",243{Path: "/tmp/bar/emptylabels.log", Labels: `{}`}: "10070",244{Path: "/tmp/bar/nolabels.log", Labels: ""}: "10060",245{Path: "/tmp/foo/emptylabels.log", Labels: `{}`}: "10050",246{Path: "/tmp/foo/nolabels.log", Labels: ""}: "10040",247}, out)248}249250func TestReadEmptyLabels(t *testing.T) {251temp := tempFilename(t)252defer func() {253_ = os.Remove(temp)254}()255256yaml := []byte(`257positions:258? path: /tmp/nolabels.log259labels: ''260: "10020"261? path: /tmp/emptylabels.log262labels: '{}'263: "10030"264? path: /tmp/missinglabels.log265: "10040"266`)267err := os.WriteFile(temp, yaml, 0644)268if err != nil {269t.Fatal(err)270}271272pos, err := readPositionsFile(Config{273PositionsFile: temp,274}, log.NewNopLogger())275276require.NoError(t, err)277require.Equal(t, "10020", pos[Entry{278Path: "/tmp/nolabels.log",279Labels: ``,280}])281require.Equal(t, "10030", pos[Entry{282Path: "/tmp/emptylabels.log",283Labels: `{}`,284}])285require.Equal(t, "10040", pos[Entry{286Path: "/tmp/missinglabels.log",287Labels: ``,288}])289}290291292