
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
500 lines
17 KiB
Go
500 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 (
|
|
"bytes"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/openstack/swift/go/hummingbird"
|
|
)
|
|
|
|
type replicationLogSaver struct {
|
|
logged []string
|
|
}
|
|
|
|
func (s *replicationLogSaver) Err(l string) error {
|
|
s.logged = append(s.logged, l)
|
|
return nil
|
|
}
|
|
|
|
func (s *replicationLogSaver) Info(l string) error {
|
|
s.logged = append(s.logged, l)
|
|
return nil
|
|
}
|
|
|
|
func (s *replicationLogSaver) Debug(l string) error {
|
|
s.logged = append(s.logged, l)
|
|
return nil
|
|
}
|
|
|
|
type FakeRing struct{}
|
|
|
|
func (r *FakeRing) GetNodes(partition uint64) (response []*hummingbird.Device) {
|
|
return nil
|
|
}
|
|
|
|
func (r *FakeRing) GetJobNodes(partition uint64, localDevice int) (response []*hummingbird.Device, handoff bool) {
|
|
return nil, false
|
|
}
|
|
|
|
func (r *FakeRing) GetPartition(account string, container string, object string) uint64 {
|
|
return 0
|
|
}
|
|
|
|
func (r *FakeRing) LocalDevices(localPort int) (devs []*hummingbird.Device, err error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (r *FakeRing) AllDevices() (devs []hummingbird.Device) {
|
|
return nil
|
|
}
|
|
|
|
func (r *FakeRing) GetMoreNodes(partition uint64) hummingbird.MoreNodes {
|
|
return nil
|
|
}
|
|
|
|
func makeReplicator(settings ...string) *Replicator {
|
|
return makeReplicatorWithFlags(settings, &flag.FlagSet{})
|
|
}
|
|
|
|
func makeReplicatorWithFlags(settings []string, flags *flag.FlagSet) *Replicator {
|
|
configString := "[object-replicator]\nmount_check=false\n"
|
|
for i := 0; i < len(settings); i += 2 {
|
|
configString += fmt.Sprintf("%s=%s\n", settings[i], settings[i+1])
|
|
}
|
|
conf, _ := hummingbird.StringConfig(configString)
|
|
replicator, _ := NewReplicator(conf, flags)
|
|
rep := replicator.(*Replicator)
|
|
rep.concurrencySem = make(chan struct{}, 1)
|
|
return rep
|
|
}
|
|
|
|
func newServer(handler http.Handler) (ts *httptest.Server, host string, port int) {
|
|
ts = httptest.NewServer(handler)
|
|
u, _ := url.Parse(ts.URL)
|
|
host, ports, _ := net.SplitHostPort(u.Host)
|
|
port, _ = strconv.Atoi(ports)
|
|
return ts, host, port
|
|
}
|
|
|
|
func setupDirectory() string {
|
|
dir, _ := ioutil.TempDir("", "")
|
|
os.MkdirAll(filepath.Join(dir, "sda", "objects", "1", "abc", "fffffffffffffffffffffffffffffabc"), 0777)
|
|
os.MkdirAll(filepath.Join(dir, "sda", "objects", "1", "abc", "00000000000000000000000000000abc"), 0777)
|
|
f, _ := os.Create(filepath.Join(dir, "sda", "objects", "1", "abc", "fffffffffffffffffffffffffffffabc", "12345.data"))
|
|
defer f.Close()
|
|
WriteMetadata(f.Fd(), map[string]string{"name": "/a/c/o", "Content-Length": "0", "Content-Type": "text/plain", "X-Timestamp": "12345.00000", "ETag": ""})
|
|
f, _ = os.Create(filepath.Join(dir, "sda", "objects", "1", "abc", "00000000000000000000000000000abc", "67890.data"))
|
|
defer f.Close()
|
|
WriteMetadata(f.Fd(), map[string]string{"name": "/a/c/o2", "Content-Length": "0", "Content-Type": "text/plain", "X-Timestamp": "12345.00000", "ETag": ""})
|
|
return dir
|
|
}
|
|
|
|
type FakeMoreNodes struct {
|
|
host string
|
|
port int
|
|
}
|
|
|
|
func (f FakeMoreNodes) Next() *hummingbird.Device {
|
|
return &hummingbird.Device{ReplicationIp: f.host, ReplicationPort: f.port, Device: "sdb"}
|
|
}
|
|
|
|
func TestCleanTemp(t *testing.T) {
|
|
driveRoot := setupDirectory()
|
|
defer os.RemoveAll(driveRoot)
|
|
os.MkdirAll(filepath.Join(driveRoot, "sda", "tmp"), 0777)
|
|
ioutil.WriteFile(filepath.Join(driveRoot, "sda", "tmp", "oldfile"), []byte(""), 0666)
|
|
ioutil.WriteFile(filepath.Join(driveRoot, "sda", "tmp", "newfile"), []byte(""), 0666)
|
|
os.Chtimes(filepath.Join(driveRoot, "sda", "tmp", "oldfile"), time.Now().Add(0-48*time.Hour), time.Now().Add(0-48*time.Hour))
|
|
replicator := makeReplicator("devices", driveRoot)
|
|
dev := &hummingbird.Device{ReplicationIp: "", ReplicationPort: 0, Device: "sda"}
|
|
replicator.cleanTemp(dev)
|
|
assert.True(t, hummingbird.Exists(filepath.Join(driveRoot, "sda", "tmp", "newfile")))
|
|
assert.False(t, hummingbird.Exists(filepath.Join(driveRoot, "sda", "tmp", "oldfile")))
|
|
}
|
|
|
|
func TestReplicatorReportStats(t *testing.T) {
|
|
saved := &replicationLogSaver{}
|
|
replicator := makeReplicator("devices", os.TempDir(), "ms_per_part", "1", "concurrency", "3")
|
|
replicator.logger = saved
|
|
reportStats := func(t time.Time) string {
|
|
c := make(chan time.Time)
|
|
done := make(chan bool)
|
|
go func() {
|
|
replicator.statsReporter(c)
|
|
done <- true
|
|
}()
|
|
replicator.jobCountIncrement <- 100
|
|
replicator.replicationCountIncrement <- 50
|
|
replicator.partitionTimesAdd <- 10.0
|
|
replicator.partitionTimesAdd <- 20.0
|
|
replicator.partitionTimesAdd <- 15.0
|
|
c <- t
|
|
close(c)
|
|
<-done
|
|
return saved.logged[len(saved.logged)-1]
|
|
}
|
|
var remaining int
|
|
var elapsed, rate float64
|
|
replicator.startTime = time.Now()
|
|
reportStats(time.Now().Add(100 * time.Second))
|
|
cnt, err := fmt.Sscanf(saved.logged[0], "50/100 (50.00%%) partitions replicated in %fs (%f/sec, %dm remaining)", &elapsed, &rate, &remaining)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, 3, cnt)
|
|
assert.Equal(t, 2, remaining)
|
|
assert.Equal(t, saved.logged[1], "Partition times: max 20.0000s, min 10.0000s, med 15.0000s")
|
|
}
|
|
|
|
type FakeLocalRing struct {
|
|
*FakeRing
|
|
dev *hummingbird.Device
|
|
}
|
|
|
|
func (r *FakeLocalRing) GetJobNodes(partition uint64, localDevice int) (response []*hummingbird.Device, handoff bool) {
|
|
return []*hummingbird.Device{r.dev}, false
|
|
}
|
|
|
|
type FakeHandoffRing struct {
|
|
*FakeRing
|
|
dev *hummingbird.Device
|
|
}
|
|
|
|
func (r *FakeHandoffRing) GetJobNodes(partition uint64, localDevice int) (response []*hummingbird.Device, handoff bool) {
|
|
return []*hummingbird.Device{r.dev}, true
|
|
}
|
|
|
|
func TestReplicatorVmDuration(t *testing.T) {
|
|
replicator := makeReplicator("vm_test_mode", "true")
|
|
assert.Equal(t, 2000*time.Millisecond, replicator.timePerPart)
|
|
}
|
|
|
|
type repmanLogSaver struct {
|
|
logged []string
|
|
}
|
|
|
|
func (s *repmanLogSaver) Err(val string) error {
|
|
s.logged = append(s.logged, val)
|
|
return nil
|
|
}
|
|
|
|
func TestGetFile(t *testing.T) {
|
|
replicator := makeReplicator()
|
|
file, err := ioutil.TempFile("", "")
|
|
assert.Nil(t, err)
|
|
defer file.Close()
|
|
defer os.RemoveAll(file.Name())
|
|
file.Write([]byte("SOME DATA"))
|
|
WriteMetadata(file.Fd(), map[string]string{
|
|
"ETag": "662411c1698ecc13dd07aee13439eadc",
|
|
"X-Timestamp": "1234567890.12345",
|
|
"Content-Length": "9",
|
|
"name": "some name",
|
|
})
|
|
|
|
fp, xattrs, size, err := replicator.getFile(file.Name())
|
|
fp.Close()
|
|
require.Equal(t, size, int64(9))
|
|
require.True(t, len(xattrs) > 0)
|
|
assert.Nil(t, err)
|
|
}
|
|
|
|
func TestGetFileBadFile(t *testing.T) {
|
|
replicator := makeReplicator()
|
|
_, _, _, err := replicator.getFile("somenonexistentfile")
|
|
require.NotNil(t, err)
|
|
|
|
dir, err := ioutil.TempDir("", "")
|
|
require.Nil(t, err)
|
|
defer os.RemoveAll(dir)
|
|
_, _, _, err = replicator.getFile(dir)
|
|
require.NotNil(t, err)
|
|
|
|
file, err := ioutil.TempFile("", "")
|
|
require.Nil(t, err)
|
|
defer file.Close()
|
|
defer os.RemoveAll(file.Name())
|
|
_, _, _, err = replicator.getFile(file.Name())
|
|
require.NotNil(t, err)
|
|
}
|
|
|
|
func TestGetFileBadMetadata(t *testing.T) {
|
|
replicator := makeReplicator()
|
|
file, err := ioutil.TempFile("", "")
|
|
require.Nil(t, err)
|
|
defer file.Close()
|
|
defer os.RemoveAll(file.Name())
|
|
|
|
require.Nil(t, RawWriteMetadata(file.Fd(), []byte("HI")))
|
|
_, _, _, err = replicator.getFile(file.Name())
|
|
require.NotNil(t, err)
|
|
|
|
require.Nil(t, RawWriteMetadata(file.Fd(), []byte("\x80\x02U\x02HIq\x01.")))
|
|
_, _, _, err = replicator.getFile(file.Name())
|
|
require.NotNil(t, err)
|
|
|
|
require.Nil(t, RawWriteMetadata(file.Fd(), []byte("\x80\x02}q\x01K\x00U\x02hiq\x02s.")))
|
|
_, _, _, err = replicator.getFile(file.Name())
|
|
require.NotNil(t, err)
|
|
|
|
require.Nil(t, RawWriteMetadata(file.Fd(), []byte("\x80\x02}q\x01U\x02hiq\x02K\x00s.")))
|
|
_, _, _, err = replicator.getFile(file.Name())
|
|
require.NotNil(t, err)
|
|
|
|
dfile, err := os.Create(file.Name() + ".data")
|
|
require.Nil(t, err)
|
|
defer file.Close()
|
|
defer os.RemoveAll(file.Name())
|
|
require.Nil(t, WriteMetadata(dfile.Fd(), nil))
|
|
_, _, _, err = replicator.getFile(dfile.Name())
|
|
require.NotNil(t, err)
|
|
|
|
tfile, err := os.Create(file.Name() + ".ts")
|
|
require.Nil(t, err)
|
|
defer file.Close()
|
|
defer os.RemoveAll(file.Name())
|
|
require.Nil(t, WriteMetadata(tfile.Fd(), nil))
|
|
_, _, _, err = replicator.getFile(tfile.Name())
|
|
require.NotNil(t, err)
|
|
|
|
dfile, err = os.Create(file.Name() + ".data")
|
|
require.Nil(t, err)
|
|
defer file.Close()
|
|
defer os.RemoveAll(file.Name())
|
|
require.Nil(t, WriteMetadata(dfile.Fd(), nil))
|
|
_, _, _, err = replicator.getFile(dfile.Name())
|
|
require.NotNil(t, err)
|
|
}
|
|
|
|
type FakeRepRing1 struct {
|
|
*FakeRing
|
|
ldev, rdev *hummingbird.Device
|
|
}
|
|
|
|
func (r *FakeRepRing1) GetJobNodes(partition uint64, localDevice int) (response []*hummingbird.Device, handoff bool) {
|
|
return []*hummingbird.Device{r.rdev}, false
|
|
}
|
|
|
|
func (r *FakeRepRing1) LocalDevices(localPort int) (devs []*hummingbird.Device, err error) {
|
|
return []*hummingbird.Device{r.ldev}, nil
|
|
}
|
|
|
|
func TestReplicationLocal(t *testing.T) {
|
|
ts, err := makeObjectServer()
|
|
assert.Nil(t, err)
|
|
defer ts.Close()
|
|
|
|
ts2, err := makeObjectServer()
|
|
assert.Nil(t, err)
|
|
defer ts2.Close()
|
|
|
|
req, err := http.NewRequest("PUT", fmt.Sprintf("http://%s:%d/sda/0/a/c/o", ts.host, ts.port),
|
|
bytes.NewBuffer([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ")))
|
|
assert.Nil(t, err)
|
|
req.Header.Set("Content-Type", "application/octet-stream")
|
|
req.Header.Set("Content-Length", "26")
|
|
req.Header.Set("X-Timestamp", hummingbird.GetTimestamp())
|
|
resp, err := http.DefaultClient.Do(req)
|
|
require.Nil(t, err)
|
|
require.Equal(t, 201, resp.StatusCode)
|
|
|
|
ldev := &hummingbird.Device{ReplicationIp: ts.host, ReplicationPort: ts.port, Device: "sda"}
|
|
rdev := &hummingbird.Device{ReplicationIp: ts2.host, ReplicationPort: ts2.port, Device: "sda"}
|
|
replicator := makeReplicator("bind_port", fmt.Sprintf("%d", ts.port))
|
|
replicator.driveRoot = ts.objServer.driveRoot
|
|
replicator.Ring = &FakeRepRing1{ldev: ldev, rdev: rdev}
|
|
replicator.Run()
|
|
|
|
req, err = http.NewRequest("HEAD", fmt.Sprintf("http://%s:%d/sda/0/a/c/o", ts2.host, ts2.port), nil)
|
|
assert.Nil(t, err)
|
|
resp, err = http.DefaultClient.Do(req)
|
|
require.Nil(t, err)
|
|
require.Equal(t, 200, resp.StatusCode)
|
|
}
|
|
|
|
type FakeRepRing2 struct {
|
|
*FakeRing
|
|
ldev, rdev *hummingbird.Device
|
|
}
|
|
|
|
func (r *FakeRepRing2) GetJobNodes(partition uint64, localDevice int) (response []*hummingbird.Device, handoff bool) {
|
|
return []*hummingbird.Device{r.rdev}, true
|
|
}
|
|
|
|
func (r *FakeRepRing2) LocalDevices(localPort int) (devs []*hummingbird.Device, err error) {
|
|
return []*hummingbird.Device{r.ldev}, nil
|
|
}
|
|
|
|
func TestReplicationHandoff(t *testing.T) {
|
|
ts, err := makeObjectServer()
|
|
assert.Nil(t, err)
|
|
defer ts.Close()
|
|
|
|
ts2, err := makeObjectServer()
|
|
assert.Nil(t, err)
|
|
defer ts2.Close()
|
|
|
|
req, err := http.NewRequest("PUT", fmt.Sprintf("http://%s:%d/sda/0/a/c/o", ts.host, ts.port),
|
|
bytes.NewBuffer([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ")))
|
|
assert.Nil(t, err)
|
|
req.Header.Set("Content-Type", "application/octet-stream")
|
|
req.Header.Set("Content-Length", "26")
|
|
req.Header.Set("X-Timestamp", hummingbird.GetTimestamp())
|
|
resp, err := http.DefaultClient.Do(req)
|
|
require.Nil(t, err)
|
|
require.Equal(t, 201, resp.StatusCode)
|
|
|
|
ldev := &hummingbird.Device{ReplicationIp: ts.host, ReplicationPort: ts.port, Device: "sda"}
|
|
rdev := &hummingbird.Device{ReplicationIp: ts2.host, ReplicationPort: ts2.port, Device: "sda"}
|
|
replicator := makeReplicator("bind_port", fmt.Sprintf("%d", ts.port))
|
|
replicator.driveRoot = ts.objServer.driveRoot
|
|
replicator.Ring = &FakeRepRing2{ldev: ldev, rdev: rdev}
|
|
replicator.Run()
|
|
|
|
req, err = http.NewRequest("HEAD", fmt.Sprintf("http://%s:%d/sda/0/a/c/o", ts2.host, ts2.port), nil)
|
|
assert.Nil(t, err)
|
|
resp, err = http.DefaultClient.Do(req)
|
|
require.Nil(t, err)
|
|
require.Equal(t, 200, resp.StatusCode)
|
|
}
|
|
|
|
func TestReplicationHandoffQuorumDelete(t *testing.T) {
|
|
ts, err := makeObjectServer()
|
|
assert.Nil(t, err)
|
|
defer ts.Close()
|
|
|
|
ts2, err := makeObjectServer()
|
|
assert.Nil(t, err)
|
|
defer ts2.Close()
|
|
|
|
req, err := http.NewRequest("PUT", fmt.Sprintf("http://%s:%d/sda/0/a/c/o", ts.host, ts.port),
|
|
bytes.NewBuffer([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ")))
|
|
assert.Nil(t, err)
|
|
req.Header.Set("Content-Type", "application/octet-stream")
|
|
req.Header.Set("Content-Length", "26")
|
|
req.Header.Set("X-Timestamp", hummingbird.GetTimestamp())
|
|
resp, err := http.DefaultClient.Do(req)
|
|
require.Nil(t, err)
|
|
require.Equal(t, 201, resp.StatusCode)
|
|
|
|
ldev := &hummingbird.Device{ReplicationIp: ts.host, ReplicationPort: ts.port, Device: "sda"}
|
|
rdev := &hummingbird.Device{ReplicationIp: ts2.host, ReplicationPort: ts2.port, Device: "sda"}
|
|
flags := flag.NewFlagSet("hbird flags", flag.ContinueOnError)
|
|
flags.Bool("q", false, "boolean value")
|
|
flags.Parse([]string{})
|
|
replicator := makeReplicatorWithFlags([]string{"bind_port", fmt.Sprintf("%d", ts.port)}, flags)
|
|
require.False(t, replicator.quorumDelete)
|
|
|
|
flags.Parse([]string{"-q"})
|
|
replicator = makeReplicatorWithFlags([]string{"bind_port", fmt.Sprintf("%d", ts.port)}, flags)
|
|
require.True(t, replicator.quorumDelete)
|
|
replicator.driveRoot = ts.objServer.driveRoot
|
|
replicator.Ring = &FakeRepRing2{ldev: ldev, rdev: rdev}
|
|
replicator.Run()
|
|
|
|
req, err = http.NewRequest("HEAD", fmt.Sprintf("http://%s:%d/sda/0/a/c/o", ts2.host, ts2.port), nil)
|
|
assert.Nil(t, err)
|
|
resp, err = http.DefaultClient.Do(req)
|
|
require.Nil(t, err)
|
|
require.Equal(t, 200, resp.StatusCode)
|
|
}
|
|
|
|
func TestListObjFiles(t *testing.T) {
|
|
dir, err := ioutil.TempDir("", "")
|
|
require.Nil(t, err)
|
|
defer os.RemoveAll(dir)
|
|
os.MkdirAll(filepath.Join(dir, "objects", "1", "abc", "d41d8cd98f00b204e9800998ecf8427e"), 0777)
|
|
fp, err := os.Create(filepath.Join(dir, "objects", "1", "abc", "d41d8cd98f00b204e9800998ecf8427e", "12345.data"))
|
|
require.Nil(t, err)
|
|
defer fp.Close()
|
|
files, err := listObjFiles(filepath.Join(dir, "objects", "1"), func(string) bool { return true })
|
|
require.Nil(t, err)
|
|
require.Equal(t, 1, len(files))
|
|
require.Equal(t, filepath.Join(dir, "objects", "1", "abc", "d41d8cd98f00b204e9800998ecf8427e", "12345.data"), files[0])
|
|
|
|
os.RemoveAll(filepath.Join(dir, "objects", "1", "abc", "d41d8cd98f00b204e9800998ecf8427e", "12345.data"))
|
|
files, err = listObjFiles(filepath.Join(dir, "objects", "1"), func(string) bool { return true })
|
|
require.False(t, hummingbird.Exists(filepath.Join(dir, "objects", "1", "abc", "d41d8cd98f00b204e9800998ecf8427e")))
|
|
require.True(t, hummingbird.Exists(filepath.Join(dir, "objects", "1", "abc")))
|
|
|
|
files, err = listObjFiles(filepath.Join(dir, "objects", "1"), func(string) bool { return true })
|
|
require.False(t, hummingbird.Exists(filepath.Join(dir, "objects", "1", "abc")))
|
|
require.True(t, hummingbird.Exists(filepath.Join(dir, "objects", "1")))
|
|
|
|
files, err = listObjFiles(filepath.Join(dir, "objects", "1"), func(string) bool { return true })
|
|
require.False(t, hummingbird.Exists(filepath.Join(dir, "objects", "1")))
|
|
require.True(t, hummingbird.Exists(filepath.Join(dir, "objects")))
|
|
}
|
|
|
|
func TestPriorityRepHandler(t *testing.T) {
|
|
t.Parallel()
|
|
driveRoot := setupDirectory()
|
|
defer os.RemoveAll(driveRoot)
|
|
replicator := makeReplicator("bind_port", "1234", "check_mounts", "no")
|
|
replicator.driveRoot = driveRoot
|
|
w := httptest.NewRecorder()
|
|
job := &PriorityRepJob{
|
|
Partition: 1,
|
|
FromDevice: &hummingbird.Device{Id: 1, Device: "sda", Ip: "127.0.0.1", Port: 5000, ReplicationIp: "127.0.0.1", ReplicationPort: 5000},
|
|
ToDevices: []*hummingbird.Device{
|
|
&hummingbird.Device{Id: 2, Device: "sdb"},
|
|
},
|
|
}
|
|
jsonned, _ := json.Marshal(job)
|
|
req, _ := http.NewRequest("POST", "/priorityrep", bytes.NewBuffer(jsonned))
|
|
go func() {
|
|
replicator.priorityRepHandler(w, req)
|
|
require.EqualValues(t, 200, w.Code)
|
|
}()
|
|
pri := <-replicator.getPriRepChan(1)
|
|
require.Equal(t, "1", strconv.FormatUint(pri.Partition, 10))
|
|
}
|
|
|
|
func TestPriorityRepHandler404(t *testing.T) {
|
|
t.Parallel()
|
|
driveRoot := setupDirectory()
|
|
defer os.RemoveAll(driveRoot)
|
|
replicator := makeReplicator("bind_port", "1234", "check_mounts", "no")
|
|
replicator.driveRoot = driveRoot
|
|
w := httptest.NewRecorder()
|
|
job := &PriorityRepJob{
|
|
Partition: 0,
|
|
FromDevice: &hummingbird.Device{Id: 1, Device: "sda", Ip: "127.0.0.1", Port: 5000, ReplicationIp: "127.0.0.1", ReplicationPort: 5000},
|
|
ToDevices: []*hummingbird.Device{
|
|
&hummingbird.Device{Id: 2, Device: "sdb"},
|
|
},
|
|
}
|
|
jsonned, _ := json.Marshal(job)
|
|
req, _ := http.NewRequest("POST", "/priorityrep", bytes.NewBuffer(jsonned))
|
|
replicator.priorityRepHandler(w, req)
|
|
require.EqualValues(t, 404, w.Code)
|
|
}
|