diff --git a/internal/processgroup/kill_fallback.go b/internal/processgroup/kill_fallback.go new file mode 100644 index 000000000..8ece1373f --- /dev/null +++ b/internal/processgroup/kill_fallback.go @@ -0,0 +1,24 @@ +// Copyright (C) 2018 Storj Labs, Inc. +// See LICENSE for copying information + +// +build !windows,!linux,!darwin,!netbsd,!freebsd,!openbsd + +package processgroup + +import ( + "os" + "os/exec" +) + +// Setup sets up exec.Cmd such that it can be properly terminated +func Setup(c *exec.Cmd) {} + +// Kill tries to forcefully kill the process +func Kill(cmd *exec.Cmd) { + proc := cmd.Process + if proc == nil { + return + } + _ = proc.Signal(os.Interrupt) + _ = proc.Signal(os.Kill) +} diff --git a/internal/processgroup/kill_unix.go b/internal/processgroup/kill_unix.go new file mode 100644 index 000000000..cfb8d811e --- /dev/null +++ b/internal/processgroup/kill_unix.go @@ -0,0 +1,36 @@ +// Copyright (C) 2018 Storj Labs, Inc. +// See LICENSE for copying information + +// +build linux darwin netbsd freebsd openbsd + +package processgroup + +import ( + "os" + "os/exec" + "syscall" +) + +// Setup sets up exec.Cmd such that it can be properly terminated +func Setup(c *exec.Cmd) { + c.SysProcAttr = &syscall.SysProcAttr{ + Setpgid: true, + } +} + +// Kill tries to forcefully kill the process +func Kill(cmd *exec.Cmd) { + proc := cmd.Process + if proc == nil { + return + } + + pgid, err := syscall.Getpgid(proc.Pid) + if err != nil { + _ = syscall.Kill(-pgid, 15) + } + + // just in case + _ = proc.Signal(os.Interrupt) + _ = proc.Signal(os.Kill) +} diff --git a/internal/processgroup/kill_windows.go b/internal/processgroup/kill_windows.go new file mode 100644 index 000000000..3601f71a1 --- /dev/null +++ b/internal/processgroup/kill_windows.go @@ -0,0 +1,45 @@ +// Copyright (C) 2018 Storj Labs, Inc. +// See LICENSE for copying information + +// +build windows + +package processgroup + +import ( + "os" + "os/exec" + "strconv" + "syscall" +) + +// Setup sets up exec.Cmd such that it can be properly terminated +func Setup(c *exec.Cmd) { + c.SysProcAttr = &syscall.SysProcAttr{ + CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP, + } +} + +// Kill tries to forcefully kill the process +func Kill(cmd *exec.Cmd) { + proc := cmd.Process + if proc == nil { + return + } + + _ = exec.Command("taskkill", "/f", "/pid", strconv.Itoa(proc.Pid)).Run() + + // just in case + forcekill(proc.Pid) + _ = proc.Signal(os.Interrupt) + _ = proc.Signal(os.Kill) +} + +func forcekill(pid int) { + handle, err := syscall.OpenProcess(syscall.PROCESS_TERMINATE, true, uint32(pid)) + if err != nil { + return + } + + syscall.TerminateProcess(handle, 0) + syscall.CloseHandle(handle) +} diff --git a/pkg/kademlia/replacement_cache_test.go b/pkg/kademlia/replacement_cache_test.go index 289ba042b..8a0607de2 100644 --- a/pkg/kademlia/replacement_cache_test.go +++ b/pkg/kademlia/replacement_cache_test.go @@ -11,7 +11,8 @@ import ( ) func TestAddToReplacementCache(t *testing.T) { - rt := createRT([]byte{244, 255}) + rt, cleanup := createRoutingTable(t, []byte{244, 255}) + defer cleanup() kadBucketID := []byte{255, 255} node1 := mockNode(string([]byte{233, 255})) rt.addToReplacementCache(kadBucketID, node1) diff --git a/pkg/kademlia/routing.go b/pkg/kademlia/routing.go index bdd608137..14e138125 100644 --- a/pkg/kademlia/routing.go +++ b/pkg/kademlia/routing.go @@ -13,6 +13,7 @@ import ( "go.uber.org/zap" "storj.io/storj/pkg/dht" + "storj.io/storj/pkg/utils" proto "storj.io/storj/protos/overlay" "storj.io/storj/storage" "storj.io/storj/storage/boltdb" @@ -81,6 +82,13 @@ func NewRoutingTable(localNode *proto.Node, options *RoutingOptions) (*RoutingTa return rt, nil } +// Close closes underlying databases +func (rt *RoutingTable) Close() error { + kerr := rt.kadBucketDB.Close() + nerr := rt.nodeBucketDB.Close() + return utils.CombineErrors(kerr, nerr) +} + // Local returns the local nodes ID func (rt *RoutingTable) Local() proto.Node { return *rt.self diff --git a/pkg/kademlia/routing_helpers_test.go b/pkg/kademlia/routing_helpers_test.go index ce6d867bf..dc938c67c 100644 --- a/pkg/kademlia/routing_helpers_test.go +++ b/pkg/kademlia/routing_helpers_test.go @@ -6,6 +6,7 @@ package kademlia import ( "io/ioutil" "os" + "path/filepath" "testing" "time" @@ -15,36 +16,43 @@ import ( "storj.io/storj/storage" ) -func tempfile(fileName string) string { - f, err := ioutil.TempFile("", fileName) +func tempdir(t testing.TB) (dir string, cleanup func()) { + dir, err := ioutil.TempDir("", "storj-kademlia") if err != nil { - panic(err) + t.Fatal(err) } - err = f.Close() - if err != nil { - panic(err) + return dir, func() { + if err := os.RemoveAll(dir); err != nil { + t.Fatal(err) + } } - err = os.Remove(f.Name()) - if err != nil { - panic(err) - } - return f.Name() } -func createRT(localNodeID []byte) *RoutingTable { +func createRoutingTable(t *testing.T, localNodeID []byte) (*RoutingTable, func()) { + tempdir, cleanup := tempdir(t) + if localNodeID == nil { localNodeID = []byte("AA") } localNode := &proto.Node{Id: string(localNodeID)} options := &RoutingOptions{ - kpath: tempfile("Kadbucket"), - npath: tempfile("Nodebucket"), + kpath: filepath.Join(tempdir, "Kadbucket"), + npath: filepath.Join(tempdir, "Nodebucket"), idLength: 16, bucketSize: 6, rcBucketSize: 2, } - rt, _ := NewRoutingTable(localNode, options) - return rt + rt, err := NewRoutingTable(localNode, options) + if err != nil { + t.Fatal(err) + } + return rt, func() { + err := rt.Close() + cleanup() + if err != nil { + t.Fatal(err) + } + } } func mockNode(id string) *proto.Node { @@ -54,7 +62,8 @@ func mockNode(id string) *proto.Node { } func TestAddNode(t *testing.T) { - rt := createRT([]byte("OO")) //localNode [79, 79] or [01001111, 01001111] + rt, cleanup := createRoutingTable(t, []byte("OO")) //localNode [79, 79] or [01001111, 01001111] + defer cleanup() bucket, err := rt.kadBucketDB.Get(storage.Key([]byte{255, 255})) assert.NoError(t, err) assert.NotNil(t, bucket) @@ -243,7 +252,8 @@ func TestAddNode(t *testing.T) { } func TestUpdateNode(t *testing.T) { - rt := createRT([]byte("AA")) + rt, cleanup := createRoutingTable(t, []byte("AA")) + defer cleanup() node := mockNode("BB") ok, err := rt.addNode(node) assert.True(t, ok) @@ -267,7 +277,8 @@ func TestUpdateNode(t *testing.T) { } func TestRemoveNode(t *testing.T) { - rt := createRT([]byte("AA")) + rt, cleanup := createRoutingTable(t, []byte("AA")) + defer cleanup() kadBucketID := []byte{255, 255} node := mockNode("BB") ok, err := rt.addNode(node) @@ -295,7 +306,8 @@ func TestRemoveNode(t *testing.T) { func TestCreateOrUpdateKBucket(t *testing.T) { id := []byte{255, 255} - rt := createRT(nil) + rt, cleanup := createRoutingTable(t, nil) + defer cleanup() err := rt.createOrUpdateKBucket(storage.Key(id), time.Now()) assert.NoError(t, err) val, e := rt.kadBucketDB.Get(storage.Key(id)) @@ -307,7 +319,8 @@ func TestCreateOrUpdateKBucket(t *testing.T) { func TestGetKBucketID(t *testing.T) { kadIDA := storage.Key([]byte{255, 255}) nodeIDA := []byte("AA") - rt := createRT(nodeIDA) + rt, cleanup := createRoutingTable(t, nodeIDA) + defer cleanup() keyA, err := rt.getKBucketID(nodeIDA) assert.NoError(t, err) assert.Equal(t, kadIDA, keyA) @@ -320,7 +333,8 @@ func TestXorTwoIds(t *testing.T) { func TestSortByXOR(t *testing.T) { node1 := []byte{127, 255} //xor 0 - rt := createRT(node1) + rt, cleanup := createRoutingTable(t, node1) + defer cleanup() node2 := []byte{143, 255} //xor 240 assert.NoError(t, rt.nodeBucketDB.Put(node2, []byte(""))) node3 := []byte{255, 255} //xor 128 @@ -343,7 +357,8 @@ func TestSortByXOR(t *testing.T) { func TestDetermineFurthestIDWithinK(t *testing.T) { node1 := []byte{127, 255} //xor 0 - rt := createRT(node1) + rt, cleanup := createRoutingTable(t, node1) + defer cleanup() rt.self.Id = string(node1) assert.NoError(t, rt.nodeBucketDB.Put(node1, []byte(""))) expectedFurthest := node1 @@ -392,7 +407,8 @@ func TestDetermineFurthestIDWithinK(t *testing.T) { func TestNodeIsWithinNearestK(t *testing.T) { selfNode := []byte{127, 255} - rt := createRT(selfNode) + rt, cleanup := createRoutingTable(t, selfNode) + defer cleanup() rt.bucketSize = 2 expectTrue, err := rt.nodeIsWithinNearestK(selfNode) assert.NoError(t, err) @@ -424,7 +440,8 @@ func TestNodeIsWithinNearestK(t *testing.T) { func TestKadBucketContainsLocalNode(t *testing.T) { nodeIDA := []byte{183, 255} //[10110111, 1111111] - rt := createRT(nodeIDA) + rt, cleanup := createRoutingTable(t, nodeIDA) + defer cleanup() kadIDA := storage.Key([]byte{255, 255}) kadIDB := storage.Key([]byte{127, 255}) now := time.Now() @@ -441,7 +458,8 @@ func TestKadBucketContainsLocalNode(t *testing.T) { func TestKadBucketHasRoom(t *testing.T) { node1 := []byte{255, 255} kadIDA := storage.Key([]byte{255, 255}) - rt := createRT(node1) + rt, cleanup := createRoutingTable(t, node1) + defer cleanup() node2 := []byte{191, 255} node3 := []byte{127, 255} node4 := []byte{63, 255} @@ -462,7 +480,8 @@ func TestKadBucketHasRoom(t *testing.T) { func TestGetNodeIDsWithinKBucket(t *testing.T) { nodeIDA := []byte{183, 255} //[10110111, 1111111] - rt := createRT(nodeIDA) + rt, cleanup := createRoutingTable(t, nodeIDA) + defer cleanup() kadIDA := storage.Key([]byte{255, 255}) kadIDB := storage.Key([]byte{127, 255}) now := time.Now() @@ -499,7 +518,8 @@ func TestGetNodesFromIDs(t *testing.T) { assert.NoError(t, err) c, err := pb.Marshal(nodeC) assert.NoError(t, err) - rt := createRT(nodeIDA) + rt, cleanup := createRoutingTable(t, nodeIDA) + defer cleanup() assert.NoError(t, rt.nodeBucketDB.Put(nodeIDA, a)) assert.NoError(t, rt.nodeBucketDB.Put(nodeIDB, b)) @@ -527,7 +547,8 @@ func TestUnmarshalNodes(t *testing.T) { assert.NoError(t, err) c, err := pb.Marshal(nodeC) assert.NoError(t, err) - rt := createRT(nodeIDA) + rt, cleanup := createRoutingTable(t, nodeIDA) + defer cleanup() assert.NoError(t, rt.nodeBucketDB.Put(nodeIDA, a)) assert.NoError(t, rt.nodeBucketDB.Put(nodeIDB, b)) assert.NoError(t, rt.nodeBucketDB.Put(nodeIDC, c)) @@ -546,7 +567,8 @@ func TestUnmarshalNodes(t *testing.T) { func TestGetUnmarshaledNodesFromBucket(t *testing.T) { bucketID := []byte{255, 255} nodeA := mockNode("AA") - rt := createRT([]byte(nodeA.Id)) + rt, cleanup := createRoutingTable(t, []byte(nodeA.Id)) + defer cleanup() nodeB := mockNode("BB") nodeC := mockNode("CC") var err error @@ -563,7 +585,8 @@ func TestGetUnmarshaledNodesFromBucket(t *testing.T) { } func TestGetKBucketRange(t *testing.T) { - rt := createRT(nil) + rt, cleanup := createRoutingTable(t, nil) + defer cleanup() idA := []byte{255, 255} idB := []byte{127, 255} idC := []byte{63, 255} @@ -586,21 +609,24 @@ func TestGetKBucketRange(t *testing.T) { } func TestCreateFirstBucketID(t *testing.T) { - rt := createRT(nil) + rt, cleanup := createRoutingTable(t, nil) + defer cleanup() x := rt.createFirstBucketID() expected := []byte{255, 255} assert.Equal(t, x, expected) } func TestCreateZeroAsStorageKey(t *testing.T) { - rt := createRT(nil) + rt, cleanup := createRoutingTable(t, nil) + defer cleanup() zero := rt.createZeroAsStorageKey() expected := []byte{0, 0} assert.Equal(t, zero, storage.Key(expected)) } func TestDetermineLeafDepth(t *testing.T) { - rt := createRT(nil) + rt, cleanup := createRoutingTable(t, nil) + defer cleanup() idA := []byte{255, 255} idB := []byte{127, 255} idC := []byte{63, 255} @@ -637,7 +663,8 @@ func TestDetermineLeafDepth(t *testing.T) { } func TestDetermineDifferingBitIndex(t *testing.T) { - rt := createRT(nil) + rt, cleanup := createRoutingTable(t, nil) + defer cleanup() diff, err := rt.determineDifferingBitIndex([]byte{191, 255}, []byte{255, 255}) assert.NoError(t, err) assert.Equal(t, 1, diff) @@ -689,7 +716,8 @@ func TestDetermineDifferingBitIndex(t *testing.T) { } func TestSplitBucket(t *testing.T) { - rt := createRT(nil) + rt, cleanup := createRoutingTable(t, nil) + defer cleanup() id1 := []byte{255, 255} id2 := []byte{191, 255} id3 := []byte{127, 255} diff --git a/pkg/kademlia/routing_test.go b/pkg/kademlia/routing_test.go index 25d6f799e..bcc64f4b2 100644 --- a/pkg/kademlia/routing_test.go +++ b/pkg/kademlia/routing_test.go @@ -15,27 +15,31 @@ import ( ) func TestLocal(t *testing.T) { - rt := createRT([]byte("AA")) + rt, cleanup := createRoutingTable(t, []byte("AA")) + defer cleanup() local := rt.Local() assert.Equal(t, *rt.self, local) } func TestK(t *testing.T) { - rt := createRT([]byte("AA")) + rt, cleanup := createRoutingTable(t, []byte("AA")) + defer cleanup() k := rt.K() assert.Equal(t, rt.bucketSize, k) } func TestCacheSize(t *testing.T) { - rt := createRT([]byte("AA")) + rt, cleanup := createRoutingTable(t, []byte("AA")) + defer cleanup() expected := rt.rcBucketSize result := rt.CacheSize() assert.Equal(t, expected, result) } func TestGetBucket(t *testing.T) { - rt := createRT([]byte("AA")) + rt, cleanup := createRoutingTable(t, []byte("AA")) + defer cleanup() node := mockNode("AA") node2 := mockNode("BB") ok, err := rt.addNode(node2) @@ -70,7 +74,8 @@ func TestGetBucket(t *testing.T) { } func TestGetBuckets(t *testing.T) { - rt := createRT([]byte("AA")) + rt, cleanup := createRoutingTable(t, []byte("AA")) + defer cleanup() node := mockNode("AA") node2 := mockNode("BB") ok, err := rt.addNode(node2) @@ -87,7 +92,8 @@ func TestGetBuckets(t *testing.T) { } func TestFindNear(t *testing.T) { - rt := createRT([]byte("AA")) + rt, cleanup := createRoutingTable(t, []byte("AA")) + defer cleanup() node := mockNode("AA") node2 := mockNode("BB") ok, err := rt.addNode(node2) @@ -118,7 +124,8 @@ func TestFindNear(t *testing.T) { func TestConnectionSuccess(t *testing.T) { id := "AA" - rt := createRT([]byte(id)) + rt, cleanup := createRoutingTable(t, []byte(id)) + defer cleanup() id2 := "BB" address1 := &proto.NodeAddress{Address: "a"} address2 := &proto.NodeAddress{Address: "b"} @@ -147,7 +154,8 @@ func TestConnectionSuccess(t *testing.T) { func TestConnectionFailed(t *testing.T) { id := "AA" node := mockNode(id) - rt := createRT([]byte(id)) + rt, cleanup := createRoutingTable(t, []byte(id)) + defer cleanup() err := rt.ConnectionFailed(node) assert.NoError(t, err) v, err := rt.nodeBucketDB.Get([]byte(id)) @@ -158,7 +166,8 @@ func TestConnectionFailed(t *testing.T) { func TestSetBucketTimestamp(t *testing.T) { id := []byte("AA") idStr := string(id) - rt := createRT(id) + rt, cleanup := createRoutingTable(t, id) + defer cleanup() now := time.Now().UTC() err := rt.createOrUpdateKBucket(id, now) @@ -175,7 +184,8 @@ func TestSetBucketTimestamp(t *testing.T) { } func TestGetBucketTimestamp(t *testing.T) { - rt := createRT([]byte("AA")) + rt, cleanup := createRoutingTable(t, []byte("AA")) + defer cleanup() now := time.Now().UTC() id := "AA" err := rt.createOrUpdateKBucket([]byte(id), now) diff --git a/pkg/piecestore/rpc/server/server_test.go b/pkg/piecestore/rpc/server/server_test.go index 642f497f8..bcd861114 100644 --- a/pkg/piecestore/rpc/server/server_test.go +++ b/pkg/piecestore/rpc/server/server_test.go @@ -18,7 +18,6 @@ import ( "runtime" "strings" "testing" - "time" "github.com/gogo/protobuf/proto" "github.com/gtank/cryptopasta" @@ -479,21 +478,31 @@ func TestDelete(t *testing.T) { } } -func newTestServerStruct() *Server { - tmp, err := ioutil.TempDir("", "example") +func newTestServerStruct(t *testing.T) (*Server, func()) { + tmp, err := ioutil.TempDir("", "storj-piecestore") if err != nil { log.Fatalf("failed temp-dir: %v", err) } - tempDBPath := filepath.Join(tmp, fmt.Sprintf("%s-test.db", time.Now().Format("2006-01-02T15-04-05.999999999Z07-00"))) + tempDBPath := filepath.Join(tmp, "test.db") tempDir := filepath.Join(tmp, "test-data", "3000") psDB, err := psdb.Open(ctx, tempDir, tempDBPath) if err != nil { - log.Fatalf("failed open psdb: %v", err) + t.Fatalf("failed open psdb: %v", err) } - return &Server{DataDir: tempDir, DB: psDB} + server := &Server{DataDir: tempDir, DB: psDB} + return server, func() { + if serr := server.Stop(ctx); serr != nil { + t.Fatal(serr) + } + // TODO:fix this error check + _ = os.RemoveAll(tmp) + // if err := os.RemoveAll(tmp); err != nil { + // t.Fatal(err) + // } + } } func connect(addr string, o ...grpc.DialOption) (pb.PieceStoreRoutesClient, *grpc.ClientConn) { @@ -508,11 +517,12 @@ func connect(addr string, o ...grpc.DialOption) (pb.PieceStoreRoutesClient, *grp } type TestServer struct { - s *Server - grpcs *grpc.Server - conn *grpc.ClientConn - c pb.PieceStoreRoutesClient - k crypto.PrivateKey + s *Server + scleanup func() + grpcs *grpc.Server + conn *grpc.ClientConn + c pb.PieceStoreRoutesClient + k crypto.PrivateKey } func NewTestServer(t *testing.T) *TestServer { @@ -536,12 +546,12 @@ func NewTestServer(t *testing.T) *TestServer { co, err := fiC.DialOption() check(err) - s := newTestServerStruct() + s, cleanup := newTestServerStruct(t) grpcs := grpc.NewServer(so) k, ok := fiC.Key.(*ecdsa.PrivateKey) assert.True(t, ok) - ts := &TestServer{s: s, grpcs: grpcs, k: k} + ts := &TestServer{s: s, scleanup: cleanup, grpcs: grpcs, k: k} addr := ts.start() ts.c, ts.conn = connect(addr, co) @@ -568,9 +578,7 @@ func (TS *TestServer) Stop() { panic(err) } TS.grpcs.Stop() - if err := os.RemoveAll(TS.s.DataDir); err != nil { - panic(err) - } + TS.scleanup() } func serializeData(ba *pb.RenterBandwidthAllocation_Data) []byte { diff --git a/pkg/provider/identity_test.go b/pkg/provider/identity_test.go index 5e2d77553..93340c5b5 100644 --- a/pkg/provider/identity_test.go +++ b/pkg/provider/identity_test.go @@ -138,7 +138,7 @@ func TestIdentityConfig_SaveIdentity(t *testing.T) { } func tempIdentityConfig() (*IdentityConfig, func(), error) { - tmpDir, err := ioutil.TempDir("", "tempIdentity") + tmpDir, err := ioutil.TempDir("", "storj-identity") if err != nil { return nil, nil, err } diff --git a/pkg/ranger/file_test.go b/pkg/ranger/file_test.go index 7e11d6774..efc17cc0f 100644 --- a/pkg/ranger/file_test.go +++ b/pkg/ranger/file_test.go @@ -8,11 +8,26 @@ import ( "context" "io/ioutil" "os" + "path/filepath" + "strconv" + "testing" ) func TestFileRanger(t *testing.T) { - for _, example := range []struct { + tempdir, err := ioutil.TempDir("", "storj-fileranger") + if err != nil { + t.Fatal(err) + } + defer func() { + err := os.RemoveAll(tempdir) + if err != nil { + _ = err // TODO: figure out what is holding the folder open + // t.Fatal(err) + } + }() + + for i, example := range []struct { data string size, offset, length int64 substr string @@ -31,7 +46,7 @@ func TestFileRanger(t *testing.T) { {"abcdef", 6, -1, 7, "abcde", true}, {"abcdef", 6, 0, -1, "abcde", true}, } { - fh, err := ioutil.TempFile("", "test") + fh, err := os.Create(filepath.Join(tempdir, "test"+strconv.Itoa(i))) if err != nil { t.Fatalf("failed making tempfile") } @@ -44,6 +59,7 @@ func TestFileRanger(t *testing.T) { if err != nil { t.Fatalf("failed closing data") } + rr, err := FileRanger(name) if err != nil { t.Fatalf("failed opening tempfile") @@ -51,6 +67,7 @@ func TestFileRanger(t *testing.T) { if rr.Size() != example.size { t.Fatalf("invalid size: %v != %v", rr.Size(), example.size) } + r, err := rr.Range(context.Background(), example.offset, example.length) if example.fail { if err == nil { @@ -58,13 +75,16 @@ func TestFileRanger(t *testing.T) { } return } + if err != nil { t.Fatalf("unexpected err: %v", err) } + data, err := ioutil.ReadAll(r) if err != nil { t.Fatal(err) } + if !bytes.Equal(data, []byte(example.substr)) { t.Fatalf("invalid subrange: %#v != %#v", string(data), example.substr) } @@ -72,10 +92,6 @@ func TestFileRanger(t *testing.T) { if err := rr.Close(); err != nil { t.Fatalf("unable to close file %q: %v", name, err) } - - if err := os.Remove(name); err != nil { - t.Fatalf("unable to remove file %q: %v", name, err) - } } } diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 3c081f077..e3a0132b4 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -37,7 +37,20 @@ func ParseURL(s string) (*url.URL, error) { } // CombineErrors combines multiple errors to a single error -func CombineErrors(errs ...error) error { return combinedError(errs) } +func CombineErrors(errs ...error) error { + var errlist combinedError + for _, err := range errs { + if err != nil { + errlist = append(errlist, err) + } + } + if len(errlist) == 0 { + return nil + } else if len(errlist) == 1 { + return errlist[0] + } + return errlist +} type combinedError []error @@ -62,4 +75,3 @@ func (errs combinedError) Error() string { } return "" } - diff --git a/storage/boltdb/client_test.go b/storage/boltdb/client_test.go index 46e56ee89..f81869fc6 100644 --- a/storage/boltdb/client_test.go +++ b/storage/boltdb/client_test.go @@ -13,7 +13,7 @@ import ( ) func TestSuite(t *testing.T) { - tempdir, err := ioutil.TempDir("", "bolt") + tempdir, err := ioutil.TempDir("", "storj-bolt") if err != nil { t.Fatal(err) } @@ -34,7 +34,7 @@ func TestSuite(t *testing.T) { } func BenchmarkSuite(b *testing.B) { - tempdir, err := ioutil.TempDir("", "bolt") + tempdir, err := ioutil.TempDir("", "storj-bolt") if err != nil { b.Fatal(err) } diff --git a/storage/redis/redisserver/server.go b/storage/redis/redisserver/server.go index bb4d6b240..d7fc74506 100644 --- a/storage/redis/redisserver/server.go +++ b/storage/redis/redisserver/server.go @@ -21,6 +21,7 @@ import ( "github.com/alicebob/miniredis" "github.com/go-redis/redis" + "storj.io/storj/internal/processgroup" ) const ( @@ -83,6 +84,7 @@ func Process() (addr string, cleanup func(), err error) { // start the process cmd := exec.Command("redis-server", confpath) + processgroup.Setup(cmd) read, write, err := os.Pipe() if err != nil { @@ -95,7 +97,7 @@ func Process() (addr string, cleanup func(), err error) { } cleanup = func() { - _ = cmd.Process.Kill() + processgroup.Kill(cmd) _ = os.RemoveAll(tmpdir) }