mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Deflake test/syscalls:connect_external_test_native.
This native test was flaking on Buildkite because it would timeout. The test itself would pass but I think a cleanup stage would hang indefinitely. Such a hang would only happen when one of the cleaners from uds.go:createPipeReader() would fail with the following log: `Failed to kick pipe reader: open /tmp/sockets930502909/pipe/out: no such device or address`. The connect_external_test does not even use pipes. It is unnecessary setup and cleanup steps. Furthermore, test logs are very noisy from these such setup and cleanup steps for tests that are not even run. Although I am not sure why failing to kick the pipe reader would cause the test to hang, I have split out the setup done in `CreateSocketTree()` into 3 parts: 1. Creating a UDS tree so that sandbox can connect to it. 2. Create connectors outside sandbox who connect to sockets created by sandbox. 3. Creating pipes outside the sandbox that sandbox can read/write to. I also split the test targets into separate tests. These setup steps are only done for their respective tests. Makes the logs more relevant, gets rid of unnecessary steps and failures and deflakes the native connect test. PiperOrigin-RevId: 511128400
This commit is contained in:
+30
-10
@@ -68,7 +68,9 @@ def _syscall_test(
|
||||
network = "none",
|
||||
file_access = "exclusive",
|
||||
overlay = False,
|
||||
add_host_communication = False,
|
||||
add_host_uds = False,
|
||||
add_host_connector = False,
|
||||
add_host_fifo = False,
|
||||
iouring = False,
|
||||
container = None,
|
||||
one_sandbox = True,
|
||||
@@ -137,7 +139,9 @@ def _syscall_test(
|
||||
"--fusefs=" + str(fusefs),
|
||||
"--file-access=" + file_access,
|
||||
"--overlay=" + str(overlay),
|
||||
"--add-host-communication=" + str(add_host_communication),
|
||||
"--add-host-uds=" + str(add_host_uds),
|
||||
"--add-host-connector=" + str(add_host_connector),
|
||||
"--add-host-fifo=" + str(add_host_fifo),
|
||||
"--strace=" + str(debug),
|
||||
"--debug=" + str(debug),
|
||||
"--container=" + str(container),
|
||||
@@ -169,7 +173,9 @@ def syscall_test(
|
||||
use_tmpfs = False,
|
||||
add_fusefs = False,
|
||||
add_overlay = False,
|
||||
add_host_communication = False,
|
||||
add_host_uds = False,
|
||||
add_host_connector = False,
|
||||
add_host_fifo = False,
|
||||
add_hostinet = False,
|
||||
one_sandbox = True,
|
||||
iouring = False,
|
||||
@@ -185,7 +191,9 @@ def syscall_test(
|
||||
use_tmpfs: use tmpfs in the defined tests.
|
||||
add_fusefs: add a fusefs test.
|
||||
add_overlay: add an overlay test.
|
||||
add_host_communication: setup UDS and pipe external communication for tests.
|
||||
add_host_uds: setup bound UDS on the host.
|
||||
add_host_connector: setup host threads to connect to bound UDS created by sandbox.
|
||||
add_host_fifo: setup FIFO files on the host.
|
||||
add_hostinet: add a hostinet test.
|
||||
one_sandbox: runs each unit test in a new sandbox instance.
|
||||
iouring: enable IO_URING support.
|
||||
@@ -203,7 +211,9 @@ def syscall_test(
|
||||
test = test,
|
||||
platform = "native",
|
||||
use_tmpfs = False,
|
||||
add_host_communication = add_host_communication,
|
||||
add_host_uds = add_host_uds,
|
||||
add_host_connector = add_host_connector,
|
||||
add_host_fifo = add_host_fifo,
|
||||
tags = tags,
|
||||
iouring = iouring,
|
||||
debug = debug,
|
||||
@@ -217,7 +227,9 @@ def syscall_test(
|
||||
test = test,
|
||||
platform = platform,
|
||||
use_tmpfs = use_tmpfs,
|
||||
add_host_communication = add_host_communication,
|
||||
add_host_uds = add_host_uds,
|
||||
add_host_connector = add_host_connector,
|
||||
add_host_fifo = add_host_fifo,
|
||||
tags = platform_tags + tags,
|
||||
iouring = iouring,
|
||||
debug = debug,
|
||||
@@ -231,7 +243,9 @@ def syscall_test(
|
||||
test = test,
|
||||
platform = default_platform,
|
||||
use_tmpfs = use_tmpfs,
|
||||
add_host_communication = add_host_communication,
|
||||
add_host_uds = add_host_uds,
|
||||
add_host_connector = add_host_connector,
|
||||
add_host_fifo = add_host_fifo,
|
||||
tags = platforms.get(default_platform, []) + tags,
|
||||
debug = debug,
|
||||
iouring = iouring,
|
||||
@@ -246,7 +260,9 @@ def syscall_test(
|
||||
platform = default_platform,
|
||||
use_tmpfs = use_tmpfs,
|
||||
network = "host",
|
||||
add_host_communication = add_host_communication,
|
||||
add_host_uds = add_host_uds,
|
||||
add_host_connector = add_host_connector,
|
||||
add_host_fifo = add_host_fifo,
|
||||
tags = platforms.get(default_platform, []) + tags,
|
||||
debug = debug,
|
||||
iouring = iouring,
|
||||
@@ -260,7 +276,9 @@ def syscall_test(
|
||||
test = test,
|
||||
platform = default_platform,
|
||||
use_tmpfs = use_tmpfs,
|
||||
add_host_communication = add_host_communication,
|
||||
add_host_uds = add_host_uds,
|
||||
add_host_connector = add_host_connector,
|
||||
add_host_fifo = add_host_fifo,
|
||||
tags = platforms.get(default_platform, []) + tags,
|
||||
iouring = iouring,
|
||||
debug = debug,
|
||||
@@ -275,7 +293,9 @@ def syscall_test(
|
||||
platform = default_platform,
|
||||
use_tmpfs = True,
|
||||
fusefs = True,
|
||||
add_host_communication = add_host_communication,
|
||||
add_host_uds = add_host_uds,
|
||||
add_host_connector = add_host_connector,
|
||||
add_host_fifo = add_host_fifo,
|
||||
tags = platforms.get(default_platform, []) + tags,
|
||||
debug = debug,
|
||||
container = container,
|
||||
|
||||
+106
-17
@@ -56,8 +56,10 @@ var (
|
||||
setupContainerPath = flag.String("setup-container", "", "path to setup_container binary (for use with --container)")
|
||||
trace = flag.Bool("trace", false, "enables all trace points")
|
||||
|
||||
addUDSTree = flag.Bool("add-host-communication", false, "expose a tree of UDS and pipe utilities to test communication with the host")
|
||||
ioUring = flag.Bool("iouring", false, "Enables IO_URING API for asynchronous I/O")
|
||||
addHostUDS = flag.Bool("add-host-uds", false, "expose a tree of UDS to test communication with the host")
|
||||
addHostConnector = flag.Bool("add-host-connector", false, "create goroutines that connect to bound UDS that will be created by sandbox")
|
||||
addHostFIFO = flag.Bool("add-host-fifo", false, "expose a tree of FIFO to test communication with the host")
|
||||
ioUring = flag.Bool("iouring", false, "Enables IO_URING API for asynchronous I/O")
|
||||
// TODO(gvisor.dev/issue/4572): properly support leak checking for runsc, and
|
||||
// set to true as the default for the test runner.
|
||||
leakCheck = flag.Bool("leak-check", false, "check for reference leaks")
|
||||
@@ -109,8 +111,8 @@ func runTestCaseNative(testBin string, tc *gtest.TestCase, args []string, t *tes
|
||||
// interpret them.
|
||||
env = filterEnv(env, []string{"TEST_SHARD_INDEX", "TEST_TOTAL_SHARDS", "GTEST_SHARD_INDEX", "GTEST_TOTAL_SHARDS"})
|
||||
|
||||
if *addUDSTree {
|
||||
socketDir, cleanup, err := uds.CreateSocketTree("/tmp")
|
||||
if *addHostUDS {
|
||||
socketDir, cleanup, err := uds.CreateBoundUDSTree("/tmp")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create socket tree: %v", err)
|
||||
}
|
||||
@@ -118,10 +120,33 @@ func runTestCaseNative(testBin string, tc *gtest.TestCase, args []string, t *tes
|
||||
|
||||
env = append(env, "TEST_UDS_TREE="+socketDir)
|
||||
// On Linux, the concept of "attach" location doesn't exist.
|
||||
// Just pass the same path to make these test identical.
|
||||
// Just pass the same path to make these tests identical.
|
||||
env = append(env, "TEST_UDS_ATTACH_TREE="+socketDir)
|
||||
}
|
||||
|
||||
if *addHostConnector {
|
||||
connectorDir, cleanup, err := uds.CreateSocketConnectors("/tmp")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create socket connectors: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
env = append(env, "TEST_CONNECTOR_TREE="+connectorDir)
|
||||
}
|
||||
|
||||
if *addHostFIFO {
|
||||
pipeDir, cleanup, err := uds.CreateFifoTree("/tmp")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create pipe tree: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
env = append(env, "TEST_FIFO_TREE="+pipeDir)
|
||||
// On Linux, the concept of "attach" location doesn't exist.
|
||||
// Just pass the same path to make these tests identical.
|
||||
env = append(env, "TEST_FIFO_ATTACH_TREE="+pipeDir)
|
||||
}
|
||||
|
||||
if *platformSupport != "" {
|
||||
env = append(env, fmt.Sprintf("%s=%s", platformSupportEnvVar, *platformSupport))
|
||||
}
|
||||
@@ -214,8 +239,14 @@ func runRunsc(tc *gtest.TestCase, spec *specs.Spec) error {
|
||||
if *strace {
|
||||
args = append(args, "-strace")
|
||||
}
|
||||
if *addUDSTree {
|
||||
args = append(args, "-host-uds=all", "-host-fifo=open")
|
||||
if *addHostUDS {
|
||||
args = append(args, "-host-uds=open")
|
||||
}
|
||||
if *addHostConnector {
|
||||
args = append(args, "-host-uds=create")
|
||||
}
|
||||
if *addHostFIFO {
|
||||
args = append(args, "-host-fifo=open")
|
||||
}
|
||||
if *leakCheck {
|
||||
args = append(args, "-ref-leak-mode=log-names")
|
||||
@@ -325,10 +356,10 @@ func runRunsc(tc *gtest.TestCase, spec *specs.Spec) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// setupHostCommTree updates the spec to expose a UDS and pipe files tree for
|
||||
// testing communication with the host.
|
||||
func setupHostCommTree(spec *specs.Spec) (cleanup func(), err error) {
|
||||
socketDir, cleanup, err := uds.CreateSocketTree("/tmp")
|
||||
// setupHostUDSTree updates the spec to expose a UDS files tree for testing
|
||||
// communication with the host.
|
||||
func setupHostUDSTree(spec *specs.Spec) (cleanup func(), err error) {
|
||||
socketDir, cleanup, err := uds.CreateBoundUDSTree("/tmp")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create socket tree: %v", err)
|
||||
}
|
||||
@@ -356,16 +387,60 @@ func setupHostCommTree(spec *specs.Spec) (cleanup func(), err error) {
|
||||
Source: filepath.Join(socketDir, "dgram/null"),
|
||||
Type: "bind",
|
||||
})
|
||||
|
||||
spec.Process.Env = append(spec.Process.Env, "TEST_UDS_TREE=/tmp/sockets")
|
||||
spec.Process.Env = append(spec.Process.Env, "TEST_UDS_ATTACH_TREE=/tmp/sockets-attach")
|
||||
|
||||
return cleanup, nil
|
||||
}
|
||||
|
||||
// setupHostFifoTree starts goroutines that will attempt to connect to sockets
|
||||
// in a directory that will be bind mounted into the container.
|
||||
func setupHostConnectorTree(spec *specs.Spec) (cleanup func(), err error) {
|
||||
connectorDir, cleanup, err := uds.CreateSocketConnectors("/tmp")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create connector tree: %v", err)
|
||||
}
|
||||
|
||||
// Standard access to entire tree.
|
||||
spec.Mounts = append(spec.Mounts, specs.Mount{
|
||||
Destination: "/tmp/connectors",
|
||||
Source: connectorDir,
|
||||
Type: "bind",
|
||||
})
|
||||
// We can not create individual attach points for sockets that have not been
|
||||
// created yet.
|
||||
spec.Process.Env = append(spec.Process.Env, "TEST_CONNECTOR_TREE=/tmp/connectors")
|
||||
return cleanup, nil
|
||||
}
|
||||
|
||||
// setupHostFifoTree updates the spec to expose FIFO file tree for testing
|
||||
// communication with the host.
|
||||
func setupHostFifoTree(spec *specs.Spec) (cleanup func(), err error) {
|
||||
fifoDir, cleanup, err := uds.CreateFifoTree("/tmp")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create FIFO tree: %v", err)
|
||||
}
|
||||
|
||||
// Standard access to entire tree.
|
||||
spec.Mounts = append(spec.Mounts, specs.Mount{
|
||||
Destination: "/tmp/pipes",
|
||||
Source: fifoDir,
|
||||
Type: "bind",
|
||||
})
|
||||
|
||||
// Individual attach points for each pipe to test mounts that attach
|
||||
// directly to the pipe.
|
||||
for _, name := range []string{"in", "out"} {
|
||||
spec.Mounts = append(spec.Mounts, specs.Mount{
|
||||
Destination: filepath.Join("/tmp/sockets-attach/pipe", name),
|
||||
Source: filepath.Join(socketDir, "pipe", name),
|
||||
Destination: filepath.Join("/tmp/pipes-attach", name),
|
||||
Source: filepath.Join(fifoDir, name),
|
||||
Type: "bind",
|
||||
})
|
||||
}
|
||||
|
||||
spec.Process.Env = append(spec.Process.Env, "TEST_UDS_TREE=/tmp/sockets")
|
||||
spec.Process.Env = append(spec.Process.Env, "TEST_UDS_ATTACH_TREE=/tmp/sockets-attach")
|
||||
spec.Process.Env = append(spec.Process.Env, "TEST_FIFO_TREE=/tmp/pipes")
|
||||
spec.Process.Env = append(spec.Process.Env, "TEST_FIFO_ATTACH_TREE=/tmp/pipes-attach")
|
||||
|
||||
return cleanup, nil
|
||||
}
|
||||
@@ -476,13 +551,27 @@ func runTestCaseRunsc(testBin string, tc *gtest.TestCase, args []string, t *test
|
||||
|
||||
spec.Process.Env = env
|
||||
|
||||
if *addUDSTree {
|
||||
cleanup, err := setupHostCommTree(spec)
|
||||
if *addHostUDS {
|
||||
cleanup, err := setupHostUDSTree(spec)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating UDS tree: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
}
|
||||
if *addHostConnector {
|
||||
cleanup, err := setupHostConnectorTree(spec)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating connector tree: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
}
|
||||
if *addHostFIFO {
|
||||
cleanup, err := setupHostFifoTree(spec)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating FIFO tree: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
}
|
||||
|
||||
if err := runRunsc(tc, spec); err != nil {
|
||||
t.Errorf("test %q failed with error %v, want nil", tc.FullName(), err)
|
||||
|
||||
+15
-5
@@ -117,21 +117,31 @@ syscall_test(
|
||||
)
|
||||
|
||||
syscall_test(
|
||||
add_host_communication = True,
|
||||
add_host_connector = True,
|
||||
add_hostinet = True,
|
||||
one_sandbox = False,
|
||||
test = "//test/syscalls/linux:connect_external_test",
|
||||
test = "//test/syscalls/linux:bind_external_test",
|
||||
# Shared mode tests replace /tmp which hides the files created for
|
||||
# add_host_communication. use_tmpfs makes shared mode be skipped.
|
||||
# add_host_connector. use_tmpfs makes shared mode be skipped.
|
||||
use_tmpfs = True,
|
||||
)
|
||||
|
||||
syscall_test(
|
||||
add_host_communication = True,
|
||||
add_host_uds = True,
|
||||
add_hostinet = True,
|
||||
one_sandbox = False,
|
||||
test = "//test/syscalls/linux:connect_external_test",
|
||||
# Shared mode tests replace /tmp which hides the files created for
|
||||
# add_host_uds. use_tmpfs makes shared mode be skipped.
|
||||
use_tmpfs = True,
|
||||
)
|
||||
|
||||
syscall_test(
|
||||
add_host_fifo = True,
|
||||
one_sandbox = False,
|
||||
test = "//test/syscalls/linux:pipe_external_test",
|
||||
# Shared mode tests replace /tmp which hides the files created for
|
||||
# add_host_communication. use_tmpfs makes shared mode be skipped.
|
||||
# add_host_fifo. use_tmpfs makes shared mode be skipped.
|
||||
use_tmpfs = True,
|
||||
)
|
||||
|
||||
|
||||
@@ -536,6 +536,21 @@ cc_binary(
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "bind_external_test",
|
||||
testonly = 1,
|
||||
srcs = ["bind_external.cc"],
|
||||
linkstatic = 1,
|
||||
deps = [
|
||||
"//test/util:file_descriptor",
|
||||
"//test/util:fs_util",
|
||||
"//test/util:socket_util",
|
||||
gtest,
|
||||
"//test/util:test_main",
|
||||
"//test/util:test_util",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "connect_external_test",
|
||||
testonly = 1,
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
// Copyright 2019 The gVisor Authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/util/file_descriptor.h"
|
||||
#include "test/util/fs_util.h"
|
||||
#include "test/util/socket_util.h"
|
||||
#include "test/util/test_util.h"
|
||||
|
||||
// This file contains tests specific to binding to host UDS that will be
|
||||
// connected to from outside the sandbox / test.
|
||||
//
|
||||
// A set of ultity sockets will be created externally in $TEST_UDS_TREE and
|
||||
// $TEST_UDS_ATTACH_TREE for these tests to interact with.
|
||||
|
||||
namespace gvisor {
|
||||
namespace testing {
|
||||
|
||||
namespace {
|
||||
|
||||
struct ProtocolSocket {
|
||||
int protocol;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
// Parameter is (socket root dir, ProtocolSocket).
|
||||
using GoferStreamSeqpacketTest =
|
||||
::testing::TestWithParam<std::tuple<std::string, ProtocolSocket>>;
|
||||
|
||||
// Bind to a socket, then Listen and Accept.
|
||||
TEST_P(GoferStreamSeqpacketTest, BindListenAccept) {
|
||||
std::string env;
|
||||
ProtocolSocket proto;
|
||||
std::tie(env, proto) = GetParam();
|
||||
|
||||
char* val = getenv(env.c_str());
|
||||
ASSERT_NE(val, nullptr);
|
||||
std::string root(val);
|
||||
|
||||
FileDescriptor sock =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_UNIX, proto.protocol, 0));
|
||||
|
||||
std::string socket_path = JoinPath(root, proto.name, "created-in-sandbox");
|
||||
|
||||
struct sockaddr_un addr = {};
|
||||
addr.sun_family = AF_UNIX;
|
||||
memcpy(addr.sun_path, socket_path.c_str(), socket_path.length());
|
||||
|
||||
ASSERT_THAT(
|
||||
bind(sock.get(), reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)),
|
||||
SyscallSucceeds());
|
||||
ASSERT_THAT(listen(sock.get(), 1), SyscallSucceeds());
|
||||
|
||||
// Bind again on that socket with a diff address should fail.
|
||||
std::string socket_path2 = socket_path + "-fail";
|
||||
struct sockaddr_un addr2 = {};
|
||||
addr2.sun_family = AF_UNIX;
|
||||
memcpy(addr2.sun_path, socket_path2.c_str(), socket_path2.length());
|
||||
ASSERT_THAT(bind(sock.get(), reinterpret_cast<struct sockaddr*>(&addr2),
|
||||
sizeof(addr2)),
|
||||
SyscallFailsWithErrno(EINVAL));
|
||||
|
||||
FileDescriptor accSock =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Accept(sock.get(), NULL, NULL));
|
||||
|
||||
// Other socket should be echo server.
|
||||
constexpr int kBufferSize = 64;
|
||||
char send_buffer[kBufferSize];
|
||||
memset(send_buffer, 'a', sizeof(send_buffer));
|
||||
|
||||
ASSERT_THAT(WriteFd(accSock.get(), send_buffer, sizeof(send_buffer)),
|
||||
SyscallSucceedsWithValue(sizeof(send_buffer)));
|
||||
|
||||
char recv_buffer[kBufferSize];
|
||||
ASSERT_THAT(ReadFd(accSock.get(), recv_buffer, sizeof(recv_buffer)),
|
||||
SyscallSucceedsWithValue(sizeof(recv_buffer)));
|
||||
ASSERT_EQ(0, memcmp(send_buffer, recv_buffer, sizeof(send_buffer)));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
StreamSeqpacket, GoferStreamSeqpacketTest,
|
||||
::testing::Combine(::testing::Values("TEST_CONNECTOR_TREE"),
|
||||
::testing::Values(ProtocolSocket{SOCK_STREAM, "stream"},
|
||||
ProtocolSocket{SOCK_SEQPACKET,
|
||||
"seqpacket"})));
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
@@ -12,17 +12,16 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <asm-generic/errno-base.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/util/file_descriptor.h"
|
||||
#include "test/util/fs_util.h"
|
||||
@@ -112,60 +111,6 @@ TEST_P(GoferStreamSeqpacketTest, NonListening) {
|
||||
SyscallFailsWithErrno(ECONNREFUSED));
|
||||
}
|
||||
|
||||
// Bind to a socket, then Listen and Accept.
|
||||
TEST_P(GoferStreamSeqpacketTest, BindListenAccept) {
|
||||
std::string env;
|
||||
ProtocolSocket proto;
|
||||
std::tie(env, proto) = GetParam();
|
||||
|
||||
// Do not parametrize this test with attach tree variant. This test creates a
|
||||
// new UDS via bind(2). It is not possible to bind mount a non-existing file.
|
||||
SKIP_IF(!strcmp("TEST_UDS_ATTACH_TREE", env.c_str()));
|
||||
|
||||
char* val = getenv(env.c_str());
|
||||
ASSERT_NE(val, nullptr);
|
||||
std::string root(val);
|
||||
|
||||
FileDescriptor sock =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_UNIX, proto.protocol, 0));
|
||||
|
||||
std::string socket_path = JoinPath(root, proto.name, "created-in-sandbox");
|
||||
|
||||
struct sockaddr_un addr = {};
|
||||
addr.sun_family = AF_UNIX;
|
||||
memcpy(addr.sun_path, socket_path.c_str(), socket_path.length());
|
||||
|
||||
ASSERT_THAT(
|
||||
bind(sock.get(), reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)),
|
||||
SyscallSucceeds());
|
||||
ASSERT_THAT(listen(sock.get(), 1), SyscallSucceeds());
|
||||
|
||||
// Bind again on that socket with a diff address should fail.
|
||||
std::string socket_path2 = socket_path + "-fail";
|
||||
struct sockaddr_un addr2 = {};
|
||||
addr2.sun_family = AF_UNIX;
|
||||
memcpy(addr2.sun_path, socket_path2.c_str(), socket_path2.length());
|
||||
ASSERT_THAT(bind(sock.get(), reinterpret_cast<struct sockaddr*>(&addr2),
|
||||
sizeof(addr2)),
|
||||
SyscallFailsWithErrno(EINVAL));
|
||||
|
||||
FileDescriptor accSock =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Accept(sock.get(), NULL, NULL));
|
||||
|
||||
// Other socket should be echo server.
|
||||
constexpr int kBufferSize = 64;
|
||||
char send_buffer[kBufferSize];
|
||||
memset(send_buffer, 'a', sizeof(send_buffer));
|
||||
|
||||
ASSERT_THAT(WriteFd(accSock.get(), send_buffer, sizeof(send_buffer)),
|
||||
SyscallSucceedsWithValue(sizeof(send_buffer)));
|
||||
|
||||
char recv_buffer[kBufferSize];
|
||||
ASSERT_THAT(ReadFd(accSock.get(), recv_buffer, sizeof(recv_buffer)),
|
||||
SyscallSucceedsWithValue(sizeof(recv_buffer)));
|
||||
ASSERT_EQ(0, memcmp(send_buffer, recv_buffer, sizeof(send_buffer)));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
StreamSeqpacket, GoferStreamSeqpacketTest,
|
||||
::testing::Combine(
|
||||
|
||||
@@ -53,7 +53,7 @@ TEST_P(HostPipeTest, Read) {
|
||||
ASSERT_NE(val, nullptr);
|
||||
const std::string root(val);
|
||||
|
||||
const std::string path = JoinPath(root, "pipe", "in");
|
||||
const std::string path = JoinPath(root, "in");
|
||||
FileDescriptor reader = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_RDONLY));
|
||||
|
||||
char lastValue = 0;
|
||||
@@ -78,7 +78,7 @@ TEST_P(HostPipeTest, Write) {
|
||||
ASSERT_NE(val, nullptr);
|
||||
const std::string root(val);
|
||||
|
||||
const std::string path = JoinPath(root, "pipe", "out");
|
||||
const std::string path = JoinPath(root, "out");
|
||||
FileDescriptor writer = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_WRONLY));
|
||||
|
||||
char lastValue = 0;
|
||||
@@ -98,8 +98,8 @@ TEST_P(HostPipeTest, Write) {
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(Paths, HostPipeTest,
|
||||
// Test access via standard path and attach point.
|
||||
::testing::Values("TEST_UDS_TREE",
|
||||
"TEST_UDS_ATTACH_TREE"));
|
||||
::testing::Values("TEST_FIFO_TREE",
|
||||
"TEST_FIFO_ATTACH_TREE"));
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
+92
-54
@@ -267,12 +267,12 @@ func createPipeReader(path string) (func(), error) {
|
||||
|
||||
cleanup := func() {
|
||||
// Kick the goroutine in case it's blocked waiting for a reader.
|
||||
if kicker, err := os.OpenFile(path, os.O_WRONLY|unix.O_NONBLOCK, 0); err != nil {
|
||||
kicker, err := os.OpenFile(path, os.O_WRONLY|unix.O_NONBLOCK, 0)
|
||||
if err != nil {
|
||||
log.Warningf("Failed to kick pipe reader: %v", err)
|
||||
return
|
||||
} else {
|
||||
_ = kicker.Close()
|
||||
}
|
||||
_ = kicker.Close()
|
||||
|
||||
reader := <-readerCh
|
||||
if reader != nil {
|
||||
@@ -285,23 +285,15 @@ func createPipeReader(path string) (func(), error) {
|
||||
}
|
||||
|
||||
type socketCreator func(path string, proto int) (cleanup func(), err error)
|
||||
type pipeCreator func(path string) (cleanup func(), err error)
|
||||
type socketCreatorSpec struct {
|
||||
protocol int
|
||||
name string
|
||||
sockets map[string]socketCreator
|
||||
}
|
||||
|
||||
// CreateSocketTree creates a local tree of unix domain sockets and pipes for
|
||||
// use in testing:
|
||||
// - /stream/echo
|
||||
// - /stream/nonlistening
|
||||
// - /seqpacket/echo
|
||||
// - /seqpacket/nonlistening
|
||||
// - /dgram/null
|
||||
// - /pipe/in
|
||||
// - /pipe/out
|
||||
//
|
||||
// Additionally, it will attempt to connect to sockets at the following
|
||||
// locations, and turn into an echo server once connected:
|
||||
// - /stream/created-in-sandbox
|
||||
// - /seqpacket/created-in-sandbox
|
||||
func CreateSocketTree(baseDir string) (string, func(), error) {
|
||||
// createSocketTree creates a local tree of unix domain sockets for use in
|
||||
// testing as per specs.
|
||||
func createSocketTree(baseDir string, specs []socketCreatorSpec) (string, func(), error) {
|
||||
dir, err := ioutil.TempDir(baseDir, "sockets")
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("error creating temp dir: %v", err)
|
||||
@@ -311,37 +303,7 @@ func CreateSocketTree(baseDir string) (string, func(), error) {
|
||||
})
|
||||
defer cu.Clean()
|
||||
|
||||
for _, proto := range []struct {
|
||||
protocol int
|
||||
name string
|
||||
sockets map[string]socketCreator
|
||||
}{
|
||||
{
|
||||
protocol: unix.SOCK_STREAM,
|
||||
name: "stream",
|
||||
sockets: map[string]socketCreator{
|
||||
"echo": createEchoSocket,
|
||||
"nonlistening": createNonListeningSocket,
|
||||
"created-in-sandbox": connectAndBecomeEcho,
|
||||
},
|
||||
},
|
||||
{
|
||||
protocol: unix.SOCK_SEQPACKET,
|
||||
name: "seqpacket",
|
||||
sockets: map[string]socketCreator{
|
||||
"echo": createEchoSocket,
|
||||
"nonlistening": createNonListeningSocket,
|
||||
"created-in-sandbox": connectAndBecomeEcho,
|
||||
},
|
||||
},
|
||||
{
|
||||
protocol: unix.SOCK_DGRAM,
|
||||
name: "dgram",
|
||||
sockets: map[string]socketCreator{
|
||||
"null": createNullSocket,
|
||||
},
|
||||
},
|
||||
} {
|
||||
for _, proto := range specs {
|
||||
protoDir := filepath.Join(dir, proto.name)
|
||||
if err := os.Mkdir(protoDir, 0755); err != nil {
|
||||
return "", nil, fmt.Errorf("error creating %s dir: %v", proto.name, err)
|
||||
@@ -357,10 +319,86 @@ func CreateSocketTree(baseDir string) (string, func(), error) {
|
||||
}
|
||||
}
|
||||
|
||||
pipeDir := filepath.Join(dir, "pipe")
|
||||
if err := os.Mkdir(pipeDir, 0755); err != nil {
|
||||
return "", nil, err
|
||||
return dir, cu.Release(), nil
|
||||
}
|
||||
|
||||
// CreateBoundUDSTree creates a local tree of bound unix domain sockets that
|
||||
// are ready to accept connections.
|
||||
//
|
||||
// These are created at locations:
|
||||
// - /stream/echo
|
||||
// - /stream/nonlistening
|
||||
// - /seqpacket/echo
|
||||
// - /seqpacket/nonlistening
|
||||
// - /dgram/null
|
||||
func CreateBoundUDSTree(baseDir string) (string, func(), error) {
|
||||
return createSocketTree(baseDir, []socketCreatorSpec{
|
||||
{
|
||||
protocol: unix.SOCK_STREAM,
|
||||
name: "stream",
|
||||
sockets: map[string]socketCreator{
|
||||
"echo": createEchoSocket,
|
||||
"nonlistening": createNonListeningSocket,
|
||||
},
|
||||
},
|
||||
{
|
||||
protocol: unix.SOCK_SEQPACKET,
|
||||
name: "seqpacket",
|
||||
sockets: map[string]socketCreator{
|
||||
"echo": createEchoSocket,
|
||||
"nonlistening": createNonListeningSocket,
|
||||
},
|
||||
},
|
||||
{
|
||||
protocol: unix.SOCK_DGRAM,
|
||||
name: "dgram",
|
||||
sockets: map[string]socketCreator{
|
||||
"null": createNullSocket,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// CreateSocketConnectors creates goroutines that will attempt to connect to
|
||||
// sockets at the following locations, and turn into an echo server once
|
||||
// connected:
|
||||
// - /stream/created-in-sandbox
|
||||
// - /seqpacket/created-in-sandbox
|
||||
func CreateSocketConnectors(baseDir string) (string, func(), error) {
|
||||
return createSocketTree(baseDir, []socketCreatorSpec{
|
||||
{
|
||||
protocol: unix.SOCK_STREAM,
|
||||
name: "stream",
|
||||
sockets: map[string]socketCreator{
|
||||
"created-in-sandbox": connectAndBecomeEcho,
|
||||
},
|
||||
},
|
||||
{
|
||||
protocol: unix.SOCK_SEQPACKET,
|
||||
name: "seqpacket",
|
||||
sockets: map[string]socketCreator{
|
||||
"created-in-sandbox": connectAndBecomeEcho,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
type pipeCreator func(path string) (cleanup func(), err error)
|
||||
|
||||
// CreateFifoTree creates a local tree of fifo files for use in testing:
|
||||
//
|
||||
// - /in
|
||||
// - /out
|
||||
func CreateFifoTree(baseDir string) (string, func(), error) {
|
||||
dir, err := ioutil.TempDir(baseDir, "pipes")
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("error creating temp dir: %v", err)
|
||||
}
|
||||
cu := cleanup.Make(func() {
|
||||
_ = os.RemoveAll(dir)
|
||||
})
|
||||
defer cu.Clean()
|
||||
|
||||
for _, pipe := range []struct {
|
||||
name string
|
||||
ctor pipeCreator
|
||||
@@ -372,7 +410,7 @@ func CreateSocketTree(baseDir string) (string, func(), error) {
|
||||
name: "out", ctor: createPipeReader,
|
||||
},
|
||||
} {
|
||||
cleanup, err := pipe.ctor(filepath.Join(pipeDir, pipe.name))
|
||||
cleanup, err := pipe.ctor(filepath.Join(dir, pipe.name))
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("error creating %q pipe: %w", pipe.name, err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user