218 lines
5.4 KiB
Go
218 lines
5.4 KiB
Go
// Licensed to Elasticsearch B.V. under one or more contributor
|
|
// license agreements. See the NOTICE file distributed with
|
|
// this work for additional information regarding copyright
|
|
// ownership. Elasticsearch B.V. licenses this file to you under
|
|
// the Apache License, Version 2.0 (the "License"); you may
|
|
// not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
|
|
// +build !integration
|
|
|
|
package checkpoint
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func eventually(t *testing.T, predicate func() (bool, error), timeout time.Duration) {
|
|
const minInterval = time.Millisecond * 5
|
|
const maxInterval = time.Millisecond * 500
|
|
|
|
checkInterval := timeout / 100
|
|
if checkInterval < minInterval {
|
|
checkInterval = minInterval
|
|
}
|
|
if checkInterval > maxInterval {
|
|
checkInterval = maxInterval
|
|
}
|
|
for deadline, first := time.Now().Add(timeout), true; first || time.Now().Before(deadline); first = false {
|
|
ok, err := predicate()
|
|
if err != nil {
|
|
t.Fatal("predicate failed with error:", err)
|
|
return
|
|
}
|
|
if ok {
|
|
return
|
|
}
|
|
time.Sleep(checkInterval)
|
|
}
|
|
t.Fatal("predicate is not true after", timeout)
|
|
}
|
|
|
|
// Test that a write is triggered when the maximum number of updates is reached.
|
|
func TestWriteMaxUpdates(t *testing.T) {
|
|
dir, err := ioutil.TempDir("", "wlb-checkpoint-test")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() {
|
|
err := os.RemoveAll(dir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}()
|
|
|
|
file := filepath.Join(dir, "some", "new", "dir", ".winlogbeat.yml")
|
|
if !assert.False(t, fileExists(file), "%s should not exist", file) {
|
|
return
|
|
}
|
|
|
|
cp, err := NewCheckpoint(file, 2, time.Hour)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer cp.Shutdown()
|
|
|
|
// Send update - it's not written to disk but it's in memory.
|
|
cp.Persist("", "", 123456, 123456)
|
|
found := false
|
|
eventually(t, func() (bool, error) {
|
|
_, found = cp.States()[""]
|
|
return found, nil
|
|
}, time.Second*15)
|
|
assert.True(t, found)
|
|
|
|
ps, err := cp.read()
|
|
if err != nil {
|
|
t.Fatal("read failed", err)
|
|
}
|
|
assert.Len(t, ps.States, 0)
|
|
|
|
// Send update - it is written to disk.
|
|
cp.Persist("", "", 123456, 123456)
|
|
eventually(t, func() (bool, error) {
|
|
ps, err = cp.read()
|
|
return ps != nil && len(ps.States) > 0, err
|
|
}, time.Second*15)
|
|
|
|
if assert.Len(t, ps.States, 1, "state not written, could be a flush timing issue, retry") {
|
|
assert.Equal(t, "", ps.States[0].Path)
|
|
assert.Equal(t, "", ps.States[0].Cursor)
|
|
}
|
|
}
|
|
|
|
// Test that a write is triggered when the maximum time period since the last
|
|
// write is reached.
|
|
func TestWriteTimedFlush(t *testing.T) {
|
|
dir, err := ioutil.TempDir("", "wlb-checkpoint-test")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() {
|
|
err := os.RemoveAll(dir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}()
|
|
|
|
file := filepath.Join(dir, ".winlogbeat.yml")
|
|
if !assert.False(t, fileExists(file), "%s should not exist", file) {
|
|
return
|
|
}
|
|
|
|
cp, err := NewCheckpoint(file, 100, time.Second)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer cp.Shutdown()
|
|
|
|
// Send update then wait longer than the flush interval and it should be
|
|
// on disk.
|
|
cp.Persist("", "cursor", 123456, 123456)
|
|
eventually(t, func() (bool, error) {
|
|
ps, err := cp.read()
|
|
return ps != nil && len(ps.States) > 0, err
|
|
}, time.Second*15)
|
|
|
|
ps, err := cp.read()
|
|
if err != nil {
|
|
t.Fatal("read failed", err)
|
|
}
|
|
if assert.Len(t, ps.States, 1) {
|
|
assert.Equal(t, "", ps.States[0].Path)
|
|
assert.Equal(t, "cursor", ps.States[0].Cursor)
|
|
}
|
|
}
|
|
|
|
// Test that createDir creates the directory with 0750 permissions.
|
|
func TestCreateDir(t *testing.T) {
|
|
dir, err := ioutil.TempDir("", "wlb-checkpoint-test")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() {
|
|
err := os.RemoveAll(dir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}()
|
|
|
|
stateDir := filepath.Join(dir, "state", "dir", "does", "not", "exists")
|
|
file := filepath.Join(stateDir, ".winlogbeat.yml")
|
|
cp := &Checkpoint{file: file}
|
|
|
|
if !assert.False(t, fileExists(file), "%s should not exist", file) {
|
|
return
|
|
}
|
|
if err = cp.createDir(); err != nil {
|
|
t.Fatal("createDir", err)
|
|
}
|
|
if !assert.True(t, fileExists(stateDir), "%s should exist", file) {
|
|
return
|
|
}
|
|
|
|
// mkdir on Windows does not pass the POSIX mode to the CreateDirectory
|
|
// syscall so doesn't test the mode.
|
|
if runtime.GOOS != "windows" {
|
|
fileInfo, err := os.Stat(stateDir)
|
|
if assert.NoError(t, err) {
|
|
assert.Equal(t, true, fileInfo.IsDir())
|
|
assert.Equal(t, os.FileMode(0750), fileInfo.Mode().Perm())
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test createDir when the directory already exists to verify that no error is
|
|
// returned.
|
|
func TestCreateDirAlreadyExists(t *testing.T) {
|
|
dir, err := ioutil.TempDir("", "wlb-checkpoint-test")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() {
|
|
err := os.RemoveAll(dir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}()
|
|
|
|
file := filepath.Join(dir, ".winlogbeat.yml")
|
|
cp := &Checkpoint{file: file}
|
|
|
|
if !assert.True(t, fileExists(dir), "%s should exist", file) {
|
|
return
|
|
}
|
|
assert.NoError(t, cp.createDir())
|
|
}
|
|
|
|
// fileExists returns true if the specified file exists.
|
|
func fileExists(file string) bool {
|
|
_, err := os.Stat(file)
|
|
return !os.IsNotExist(err)
|
|
}
|