| package main | |
| import ( | |
| "bytes" | |
| "encoding/json" | |
| "fmt" | |
| "os" | |
| "strconv" | |
| "testing" | |
| "github.com/docker/docker/daemon/config" | |
| "github.com/moby/sys/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() { | |
| cfg := &config.Config{ | |
| CommonConfig: config.CommonConfig{ | |
| Hosts: []string{"fd://"}, | |
| }, | |
| } | |
| _, _, err := loadListeners(cfg, 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, "") | |
| } |