Files
swift/go/objectserver/auditor_test.go
Michael Barton ca9591de34 go: conf.d support + some refactoring
Refactor how configs are loaded and passed to daemons.

Add conf.d support. So you can have an /etc/swift/object-server.conf.d
or /etc/swift/object-server/1.conf.d, and any .conf files under it
will be loaded as one config.

conf.d files are combined in filename lexigraphical order, with any
sections in later files replace existing section entries.

Change-Id: I78d7e5449cd0b62df9dfdb25616a2d4c91159f8c
2016-05-03 15:58:47 +00:00

425 lines
17 KiB
Go

// Copyright (c) 2015 Rackspace
//
// Licensed 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.
package objectserver
import (
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/openstack/swift/go/hummingbird"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAuditHashPasses(t *testing.T) {
dir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(dir)
os.MkdirAll(filepath.Join(dir, "fffffffffffffffffffffffffffffabc"), 0777)
f, _ := os.Create(filepath.Join(dir, "fffffffffffffffffffffffffffffabc", "12345.data"))
defer f.Close()
WriteMetadata(f.Fd(), map[string]string{"Content-Length": "12", "ETag": "d3ac5112fe464b81184352ccba743001", "name": "", "Content-Type": "", "X-Timestamp": ""})
f.Write([]byte("testcontents"))
f, _ = os.Create(filepath.Join(dir, "fffffffffffffffffffffffffffffabc", "12346.ts"))
defer f.Close()
WriteMetadata(f.Fd(), map[string]string{"name": "somename", "X-Timestamp": ""})
bytesProcessed, err := auditHash(filepath.Join(dir, "fffffffffffffffffffffffffffffabc"), false)
assert.Nil(t, err)
assert.Equal(t, bytesProcessed, int64(12))
}
func TestAuditNonStringKey(t *testing.T) {
dir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(dir)
os.MkdirAll(filepath.Join(dir, "fffffffffffffffffffffffffffffabc"), 0777)
f, _ := os.Create(filepath.Join(dir, "fffffffffffffffffffffffffffffabc", "12345.data"))
defer f.Close()
metadata := map[interface{}]string{"Content-Length": "12", "ETag": "d3ac5112fe464b81184352ccba743001", "Content-Type": "", "X-Timestamp": "", "name": "", 3: "hi"}
RawWriteMetadata(f.Fd(), hummingbird.PickleDumps(metadata))
f.Write([]byte("testcontents"))
bytesProcessed, err := auditHash(filepath.Join(dir, "fffffffffffffffffffffffffffffabc"), false)
assert.NotNil(t, err)
assert.Equal(t, bytesProcessed, int64(0))
}
func TestAuditNonStringValue(t *testing.T) {
dir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(dir)
os.MkdirAll(filepath.Join(dir, "fffffffffffffffffffffffffffffabc"), 0777)
f, _ := os.Create(filepath.Join(dir, "fffffffffffffffffffffffffffffabc", "12345.data"))
defer f.Close()
metadata := map[string]interface{}{"Content-Length": 12, "ETag": "d3ac5112fe464b81184352ccba743001", "Content-Type": "", "X-Timestamp": "", "name": ""}
RawWriteMetadata(f.Fd(), hummingbird.PickleDumps(metadata))
f.Write([]byte("testcontents"))
bytesProcessed, err := auditHash(filepath.Join(dir, "fffffffffffffffffffffffffffffabc"), false)
assert.NotNil(t, err)
assert.Equal(t, bytesProcessed, int64(0))
}
func TestAuditHashDataMissingMetadata(t *testing.T) {
dir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(dir)
os.MkdirAll(filepath.Join(dir, "fffffffffffffffffffffffffffffabc"), 0777)
f, _ := os.Create(filepath.Join(dir, "fffffffffffffffffffffffffffffabc", "12345.data"))
defer f.Close()
WriteMetadata(f.Fd(), map[string]string{"Content-Length": "12", "ETag": "d3ac5112fe464b81184352ccba743001", "Content-Type": "", "X-Timestamp": ""})
f.Write([]byte("testcontents"))
bytesProcessed, err := auditHash(filepath.Join(dir, "fffffffffffffffffffffffffffffabc"), false)
assert.NotNil(t, err)
assert.Equal(t, bytesProcessed, int64(0))
}
func TestAuditHashTSMissingMetadata(t *testing.T) {
dir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(dir)
os.MkdirAll(filepath.Join(dir, "fffffffffffffffffffffffffffffabc"), 0777)
f, _ := os.Create(filepath.Join(dir, "fffffffffffffffffffffffffffffabc", "12345.ts"))
defer f.Close()
WriteMetadata(f.Fd(), map[string]string{"name": "somename"})
bytesProcessed, err := auditHash(filepath.Join(dir, "fffffffffffffffffffffffffffffabc"), false)
assert.NotNil(t, err)
assert.Equal(t, bytesProcessed, int64(0))
}
func TestAuditHashIncorrectContentLength(t *testing.T) {
dir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(dir)
os.MkdirAll(filepath.Join(dir, "fffffffffffffffffffffffffffffabc"), 0777)
f, _ := os.Create(filepath.Join(dir, "fffffffffffffffffffffffffffffabc", "12345.data"))
defer f.Close()
WriteMetadata(f.Fd(), map[string]string{"Content-Length": "0", "ETag": "d3ac5112fe464b81184352ccba743001", "name": "", "Content-Type": "", "X-Timestamp": ""})
f.Write([]byte("testcontents"))
bytesProcessed, err := auditHash(filepath.Join(dir, "fffffffffffffffffffffffffffffabc"), false)
assert.NotNil(t, err)
assert.Equal(t, bytesProcessed, int64(0))
}
func TestAuditHashInvalidContentLength(t *testing.T) {
dir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(dir)
os.MkdirAll(filepath.Join(dir, "fffffffffffffffffffffffffffffabc"), 0777)
f, _ := os.Create(filepath.Join(dir, "fffffffffffffffffffffffffffffabc", "12345.data"))
defer f.Close()
WriteMetadata(f.Fd(), map[string]string{"Content-Length": "X", "ETag": "d3ac5112fe464b81184352ccba743001", "name": "", "Content-Type": "", "X-Timestamp": ""})
f.Write([]byte("testcontents"))
bytesProcessed, err := auditHash(filepath.Join(dir, "fffffffffffffffffffffffffffffabc"), false)
assert.NotNil(t, err)
assert.Equal(t, bytesProcessed, int64(0))
}
func TestAuditHashBadHash(t *testing.T) {
dir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(dir)
os.MkdirAll(filepath.Join(dir, "fffffffffffffffffffffffffffffabc"), 0777)
f, _ := os.Create(filepath.Join(dir, "fffffffffffffffffffffffffffffabc", "12345.data"))
defer f.Close()
WriteMetadata(f.Fd(), map[string]string{"Content-Length": "12", "ETag": "f3ac5112fe464b81184352ccba743001", "name": "", "Content-Type": "", "X-Timestamp": ""})
f.Write([]byte("testcontents"))
bytesProcessed, err := auditHash(filepath.Join(dir, "fffffffffffffffffffffffffffffabc"), false)
assert.NotNil(t, err)
assert.Equal(t, bytesProcessed, int64(12))
}
func TestAuditHashBadFilename(t *testing.T) {
dir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(dir)
os.MkdirAll(filepath.Join(dir, "fffffffffffffffffffffffffffffabc"), 0777)
f, _ := os.Create(filepath.Join(dir, "fffffffffffffffffffffffffffffabc", "12345.xxx"))
defer f.Close()
WriteMetadata(f.Fd(), map[string]string{"Content-Length": "12", "ETag": "d3ac5112fe464b81184352ccba743001", "name": "", "Content-Type": "", "X-Timestamp": ""})
f.Write([]byte("testcontents"))
bytesProcessed, err := auditHash(filepath.Join(dir, "fffffffffffffffffffffffffffffabc"), false)
assert.NotNil(t, err)
assert.Equal(t, bytesProcessed, int64(0))
}
func TestAuditHashNonfileInDir(t *testing.T) {
dir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(dir)
os.MkdirAll(filepath.Join(dir, "fffffffffffffffffffffffffffffabc", "12345.data"), 0777)
bytesProcessed, err := auditHash(filepath.Join(dir, "fffffffffffffffffffffffffffffabc"), false)
assert.NotNil(t, err)
assert.Equal(t, bytesProcessed, int64(0))
}
func TestAuditHashNoMetadata(t *testing.T) {
dir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(dir)
os.MkdirAll(filepath.Join(dir, "fffffffffffffffffffffffffffffabc"), 0777)
f, _ := os.Create(filepath.Join(dir, "fffffffffffffffffffffffffffffabc", "12345.data"))
defer f.Close()
f.Write([]byte("testcontents"))
bytesProcessed, err := auditHash(filepath.Join(dir, "fffffffffffffffffffffffffffffabc"), false)
assert.NotNil(t, err)
assert.Equal(t, bytesProcessed, int64(0))
}
type auditLogSaver struct {
logged []string
}
func (s *auditLogSaver) Err(line string) error {
s.logged = append(s.logged, line)
return nil
}
func (s *auditLogSaver) Info(line string) error {
s.logged = append(s.logged, line)
return nil
}
func (s *auditLogSaver) Debug(line string) error {
s.logged = append(s.logged, line)
return nil
}
func makeAuditor(settings ...string) *Auditor {
configString := "[object-auditor]\n"
for i := 0; i < len(settings); i += 2 {
configString += fmt.Sprintf("%s=%s\n", settings[i], settings[i+1])
}
conf, _ := hummingbird.StringConfig(configString)
auditorDaemon, _ := NewAuditor(conf, &flag.FlagSet{})
auditorDaemon.(*AuditorDaemon).logger = &auditLogSaver{}
return &Auditor{AuditorDaemon: auditorDaemon.(*AuditorDaemon), filesPerSecond: 1}
}
func TestFailsWithoutSection(t *testing.T) {
conf, err := hummingbird.StringConfig("")
require.Nil(t, err)
auditorDaemon, err := NewAuditor(conf, &flag.FlagSet{})
require.NotNil(t, err)
assert.Nil(t, auditorDaemon)
assert.True(t, strings.HasPrefix(err.Error(), "Unable to find object-auditor"))
}
func TestAuditSuffixNotDir(t *testing.T) {
auditor := makeAuditor()
auditor.logger = &auditLogSaver{}
file, _ := ioutil.TempFile("", "")
defer file.Close()
defer os.RemoveAll(file.Name())
errors := auditor.errors
auditor.auditSuffix(file.Name())
assert.True(t, strings.HasPrefix(auditor.logger.(*auditLogSaver).logged[0], "Error reading suffix dir"))
assert.True(t, auditor.errors > errors)
}
func TestAuditSuffixPasses(t *testing.T) {
dir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(dir)
os.MkdirAll(filepath.Join(dir, "abc", "fffffffffffffffffffffffffffffabc"), 0777)
f, _ := os.Create(filepath.Join(dir, "abc", "fffffffffffffffffffffffffffffabc", "12345.data"))
defer f.Close()
WriteMetadata(f.Fd(), map[string]string{"Content-Length": "12", "ETag": "d3ac5112fe464b81184352ccba743001", "name": "", "Content-Type": "", "X-Timestamp": ""})
f.Write([]byte("testcontents"))
auditor := makeAuditor()
totalPasses := auditor.totalPasses
auditor.auditSuffix(filepath.Join(dir, "abc"))
assert.Equal(t, totalPasses+1, auditor.totalPasses)
assert.Equal(t, int64(12), auditor.totalBytes)
}
func TestAuditSuffixQuarantine(t *testing.T) {
dir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(dir)
os.MkdirAll(filepath.Join(dir, "objects", "1", "abc", "fffffffffffffffffffffffffffffabc"), 0777)
f, _ := os.Create(filepath.Join(dir, "objects", "1", "abc", "fffffffffffffffffffffffffffffabc", "12345.derp"))
defer f.Close()
auditor := makeAuditor()
totalQuarantines := auditor.totalQuarantines
auditor.auditSuffix(filepath.Join(dir, "objects", "1", "abc"))
assert.Equal(t, totalQuarantines+1, auditor.totalQuarantines)
quarfiles, err := ioutil.ReadDir(filepath.Join(dir, "quarantined"))
assert.Nil(t, err)
assert.Equal(t, 1, len(quarfiles))
}
func TestAuditSuffixSkipsBad(t *testing.T) {
dir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(dir)
os.MkdirAll(filepath.Join(dir, "objects", "1", "abc", "notavalidhash"), 0777)
auditor := makeAuditor()
auditor.auditSuffix(filepath.Join(dir, "objects", "1", "abc"))
assert.True(t, strings.HasPrefix(auditor.logger.(*auditLogSaver).logged[0], "Skipping invalid file in suffix"))
}
func TestAuditPartitionNotDir(t *testing.T) {
auditor := makeAuditor()
auditor.logger = &auditLogSaver{}
file, _ := ioutil.TempFile("", "")
defer file.Close()
defer os.RemoveAll(file.Name())
errors := auditor.errors
auditor.auditPartition(file.Name())
assert.True(t, strings.HasPrefix(auditor.logger.(*auditLogSaver).logged[0], "Error reading partition dir"))
assert.True(t, auditor.errors > errors)
}
func TestAuditPartitionPasses(t *testing.T) {
dir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(dir)
os.MkdirAll(filepath.Join(dir, "1", "abc", "fffffffffffffffffffffffffffffabc"), 0777)
f, _ := os.Create(filepath.Join(dir, "1", "abc", "fffffffffffffffffffffffffffffabc", "12345.data"))
defer f.Close()
WriteMetadata(f.Fd(), map[string]string{"Content-Length": "12", "ETag": "d3ac5112fe464b81184352ccba743001", "name": "", "Content-Type": "", "X-Timestamp": ""})
f.Write([]byte("testcontents"))
auditor := makeAuditor()
totalPasses := auditor.totalPasses
auditor.auditPartition(filepath.Join(dir, "1"))
assert.Equal(t, totalPasses+1, auditor.totalPasses)
assert.Equal(t, int64(12), auditor.totalBytes)
}
func TestAuditPartitionSkipsBadData(t *testing.T) {
dir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(dir)
os.MkdirAll(filepath.Join(dir, "1", "abc", "fffffffffffffffffffffffffffffabc"), 0777)
os.MkdirAll(filepath.Join(dir, "1", "xyz"), 0777)
f, _ := os.Create(filepath.Join(dir, "1", ".lock"))
f.Close()
f, _ = os.Create(filepath.Join(dir, "1", "hashes.pkl"))
f.Close()
f, _ = os.Create(filepath.Join(dir, "1", "abc", "fffffffffffffffffffffffffffffabc", "12345.data"))
defer f.Close()
WriteMetadata(f.Fd(), map[string]string{"Content-Length": "12", "ETag": "d3ac5112fe464b81184352ccba743001", "name": "", "Content-Type": "", "X-Timestamp": ""})
f.Write([]byte("testcontents"))
auditor := makeAuditor()
totalPasses := auditor.totalPasses
auditor.auditPartition(filepath.Join(dir, "1"))
assert.Equal(t, totalPasses+1, auditor.totalPasses)
assert.Equal(t, int64(12), auditor.totalBytes)
}
func TestAuditDeviceNotDir(t *testing.T) {
auditor := makeAuditor("mount_check", "false")
auditor.logger = &auditLogSaver{}
file, _ := ioutil.TempFile("", "")
defer file.Close()
defer os.RemoveAll(file.Name())
errors := auditor.errors
auditor.auditDevice(file.Name())
assert.True(t, strings.HasPrefix(auditor.logger.(*auditLogSaver).logged[0], "Error reading objects dir"))
assert.True(t, auditor.errors > errors)
}
func TestAuditDevicePasses(t *testing.T) {
dir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(dir)
os.MkdirAll(filepath.Join(dir, "sda", "objects", "1", "abc", "fffffffffffffffffffffffffffffabc"), 0777)
f, _ := os.Create(filepath.Join(dir, "sda", "objects", "1", "abc", "fffffffffffffffffffffffffffffabc", "12345.data"))
defer f.Close()
WriteMetadata(f.Fd(), map[string]string{"Content-Length": "12", "ETag": "d3ac5112fe464b81184352ccba743001", "name": "", "Content-Type": "", "X-Timestamp": ""})
f.Write([]byte("testcontents"))
auditor := makeAuditor("mount_check", "false")
totalPasses := auditor.totalPasses
auditor.auditDevice(filepath.Join(dir, "sda"))
assert.Equal(t, totalPasses+1, auditor.totalPasses)
assert.Equal(t, int64(12), auditor.totalBytes)
}
func TestAuditDeviceSkipsBadData(t *testing.T) {
dir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(dir)
os.MkdirAll(filepath.Join(dir, "sda", "objects", "1", "abc", "fffffffffffffffffffffffffffffabc"), 0777)
os.MkdirAll(filepath.Join(dir, "sda", "objects", "X"), 0777)
f, _ := os.Create(filepath.Join(dir, "sda", "objects", "1", "abc", "fffffffffffffffffffffffffffffabc", "12345.data"))
defer f.Close()
WriteMetadata(f.Fd(), map[string]string{"Content-Length": "12", "ETag": "d3ac5112fe464b81184352ccba743001", "name": "", "Content-Type": "", "X-Timestamp": ""})
f.Write([]byte("testcontents"))
auditor := makeAuditor("mount_check", "false")
totalPasses := auditor.totalPasses
auditor.auditDevice(filepath.Join(dir, "sda"))
assert.Equal(t, totalPasses+1, auditor.totalPasses)
assert.Equal(t, int64(12), auditor.totalBytes)
}
func TestAuditDeviceUnmounted(t *testing.T) {
dir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(dir)
os.MkdirAll(filepath.Join(dir, "sda", "objects", "1"), 0777)
auditor := makeAuditor("mount_check", "true")
auditor.auditDevice(filepath.Join(dir, "sda"))
assert.True(t, strings.HasPrefix(auditor.logger.(*auditLogSaver).logged[0], "Skipping unmounted device"))
}
func TestFinalLog(t *testing.T) {
auditor := makeAuditor()
logger := &auditLogSaver{}
auditor.logger = logger
auditor.passStart = time.Now().Add(-60 * time.Second)
auditor.totalQuarantines = 5
auditor.totalErrors = 3
auditor.totalPasses = 120
auditor.totalBytes = 120000
auditor.auditorType = "ALL"
auditor.mode = "forever"
auditor.finalLog()
assert.Equal(t, "Object audit (ALL) \"forever\" mode completed: 60.00s. Total quarantined: 5, Total errors: 3, Total files/sec: 2.00, "+
"Total bytes/sec: 2000.00, Auditing time: 0.00, Rate: 0.00", logger.logged[0])
}
func TestAuditRun(t *testing.T) {
dir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(dir)
os.MkdirAll(filepath.Join(dir, "sda", "objects", "1", "abc", "fffffffffffffffffffffffffffffabc"), 0777)
f, _ := os.Create(filepath.Join(dir, "sda", "objects", "1", "abc", "fffffffffffffffffffffffffffffabc", "12345.data"))
defer f.Close()
WriteMetadata(f.Fd(), map[string]string{"Content-Length": "12", "ETag": "d3ac5112fe464b81184352ccba743001", "name": "", "Content-Type": "", "X-Timestamp": ""})
f.Write([]byte("testcontents"))
auditor := makeAuditor("mount_check", "false")
auditor.driveRoot = dir
logger := &auditLogSaver{}
auditor.logger = logger
totalPasses := auditor.totalPasses
auditor.run(OneTimeChan())
assert.Equal(t, totalPasses+1, auditor.totalPasses)
assert.Equal(t, int64(12), auditor.totalBytes)
}
func TestStatReport(t *testing.T) {
auditor := makeAuditor("mount_check", "false")
auditor.passStart = time.Now().Add(-120 * time.Second)
auditor.lastLog = time.Now().Add(-120 * time.Second)
auditor.passes = 120
auditor.bytesProcessed = 120000
auditor.quarantines = 17
auditor.errors = 41
auditor.statsReport()
args := [13]float64{}
var stra, strb string
pargs := []interface{}{&stra, &strb}
for i := range args {
pargs = append(pargs, &args[i])
}
fmt.Sscanf(
auditor.logger.(*auditLogSaver).logged[0],
"Object audit (). Since %s %s %f %f:%f:%f %f: Locally: %f passed, %f quarantined, %f errors, files/sec: %f , bytes/sec: %f, Total time: %f, Auditing time: %f, Rate: %f",
pargs...)
assert.Equal(t, 120.0, args[5])
assert.Equal(t, 17.0, args[6])
assert.Equal(t, 41.0, args[7])
assert.Equal(t, 1.0, args[8])
assert.Equal(t, 1000.0, args[9])
assert.Equal(t, 120.0, args[10])
}