Fix listener parsering regression when no addr set
5008409b5c
introduced the usage of
`strings.Cut` to help parse listener addresses.
Part of that also made it error out if no addr is specified after the
protocol spec (e.g. `tcp://`).
Before the change a proto spec without an address just used the default
address for that proto.
e.g. `tcp://` would be `tcp://127.0.0.1:2375`, `unix://` would be
`unix:///var/run/docker.sock`.
Critically, socket activation (`fd://`) never has an address.
This change brings back the old behavior but keeps the usage of
`strings.Cut`.
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
parent
6f14c8ee5a
commit
37a9d6aabe
3 changed files with 107 additions and 1 deletions
|
@ -655,7 +655,7 @@ func loadListeners(cli *DaemonCli, tlsConfig *tls.Config) ([]string, error) {
|
|||
for i := 0; i < len(cli.Config.Hosts); i++ {
|
||||
protoAddr := cli.Config.Hosts[i]
|
||||
proto, addr, ok := strings.Cut(protoAddr, "://")
|
||||
if !ok || addr == "" {
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("bad format %s, expected PROTO://ADDR", protoAddr)
|
||||
}
|
||||
|
||||
|
|
90
cmd/dockerd/daemon_linux_test.go
Normal file
90
cmd/dockerd/daemon_linux_test.go
Normal file
|
@ -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, "")
|
||||
}
|
16
cmd/dockerd/main_linux_test.go
Normal file
16
cmd/dockerd/main_linux_test.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/pkg/reexec"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
reexec.Register(testListenerNoAddrCmdPhase1, initListenerTestPhase1)
|
||||
reexec.Register(testListenerNoAddrCmdPhase2, initListenerTestPhase2)
|
||||
if reexec.Init() {
|
||||
return
|
||||
}
|
||||
m.Run()
|
||||
}
|
Loading…
Add table
Reference in a new issue