|
@@ -0,0 +1,90 @@
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "bytes"
|
|
|
+ "encoding/json"
|
|
|
+ "fmt"
|
|
|
+ "os"
|
|
|
+ "strconv"
|
|
|
+ "testing"
|
|
|
+
|
|
|
+ "github.com/docker/docker/daemon/config"
|
|
|
+ "github.com/docker/docker/pkg/reexec"
|
|
|
+ "golang.org/x/sys/unix"
|
|
|
+ "gotest.tools/v3/assert"
|
|
|
+)
|
|
|
+
|
|
|
+const (
|
|
|
+ testListenerNoAddrCmdPhase1 = "test-listener-no-addr1"
|
|
|
+ testListenerNoAddrCmdPhase2 = "test-listener-no-addr2"
|
|
|
+)
|
|
|
+
|
|
|
+type listenerTestResponse struct {
|
|
|
+ Err string
|
|
|
+}
|
|
|
+
|
|
|
+func initListenerTestPhase1() {
|
|
|
+ os.Setenv("LISTEN_PID", strconv.Itoa(os.Getpid()))
|
|
|
+ os.Setenv("LISTEN_FDS", "1")
|
|
|
+
|
|
|
+ // NOTE: We cannot use O_CLOEXEC here because we need the fd to stay open for the child process.
|
|
|
+ _, err := unix.Socket(unix.AF_UNIX, unix.SOCK_STREAM, 0)
|
|
|
+ if err != nil {
|
|
|
+ fmt.Fprintln(os.Stderr, err)
|
|
|
+ os.Exit(1)
|
|
|
+ }
|
|
|
+
|
|
|
+ cmd := reexec.Command(testListenerNoAddrCmdPhase2)
|
|
|
+ if err := unix.Exec(cmd.Path, cmd.Args, os.Environ()); err != nil {
|
|
|
+ fmt.Fprintln(os.Stderr, err)
|
|
|
+ os.Exit(1)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func initListenerTestPhase2() {
|
|
|
+ cli := &DaemonCli{
|
|
|
+ Config: &config.Config{
|
|
|
+ CommonConfig: config.CommonConfig{
|
|
|
+ Hosts: []string{"fd://"},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+ _, err := loadListeners(cli, nil)
|
|
|
+ var resp listenerTestResponse
|
|
|
+ if err != nil {
|
|
|
+ resp.Err = err.Error()
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := json.NewEncoder(os.Stdout).Encode(resp); err != nil {
|
|
|
+ fmt.Fprintln(os.Stderr, err)
|
|
|
+ os.Exit(1)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Test to make sure that the listen specs without an address are handled
|
|
|
+// It requires a 2-phase setup due to how socket activation works (which we are using to test).
|
|
|
+// It requires LISTEN_FDS and LISTEN_PID to be set in the environment.
|
|
|
+//
|
|
|
+// LISTEN_PID is used by socket activation to determine if the process is the one that should be activated.
|
|
|
+// LISTEN_FDS is used by socket activation to determine how many file descriptors are passed to the process.
|
|
|
+//
|
|
|
+// We can sort of fake this without using extra processes, but it ends up not
|
|
|
+// being a true test because that's not how socket activation is expected to
|
|
|
+// work and we'll end up with nil listeners since the test framework has other
|
|
|
+// file descriptors open.
|
|
|
+//
|
|
|
+// This is not currently testing `tcp://` or `unix://` listen specs without an address because those can conflict with the machine running the test.
|
|
|
+// This could be worked around by using linux namespaces, however that would require root privileges which unit tests don't typically have.
|
|
|
+func TestLoadListenerNoAddr(t *testing.T) {
|
|
|
+ cmd := reexec.Command(testListenerNoAddrCmdPhase1)
|
|
|
+ stdout := bytes.NewBuffer(nil)
|
|
|
+ cmd.Stdout = stdout
|
|
|
+ stderr := bytes.NewBuffer(nil)
|
|
|
+ cmd.Stderr = stderr
|
|
|
+
|
|
|
+ assert.NilError(t, cmd.Run(), stderr.String())
|
|
|
+
|
|
|
+ var resp listenerTestResponse
|
|
|
+ assert.NilError(t, json.NewDecoder(stdout).Decode(&resp))
|
|
|
+ assert.Equal(t, resp.Err, "")
|
|
|
+}
|