Files
moby/extpoints/daemonapi/v0/client_test.go
T
Paweł Gronowski be285dcad8 daemon: Create the extension API socket on first use
Every daemon with an extension host opened an extra full-access API
socket at startup, even when no extension used the API.

The socket is now created when an extension first connects to the API.
The daemonapi client looks up the endpoint on its first connection, so
NewClient no longer takes a context.

The socket is served separately from the configured hosts, returns 503
until the daemon is ready, and closes after extensions shut down.
It is accessible only to the daemon user, or to Administrators and
LocalSystem on Windows.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-11-04 19:48:07 +01:00

61 lines
1.5 KiB
Go

package daemonapiv0_test
import (
"context"
"net"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"sync/atomic"
"testing"
"github.com/moby/moby/client"
daemonapiv0 "github.com/moby/moby/v2/extpoints/daemonapi/v0"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
type fakeAPI struct {
host string
calls atomic.Int32
}
func (f *fakeAPI) Get(context.Context, *daemonapiv0.GetRequest) (*daemonapiv0.GetResponse, error) {
f.calls.Add(1)
return &daemonapiv0.GetResponse{Host: f.host}, nil
}
func TestNewClientGetsEndpointOnFirstConnect(t *testing.T) {
t.Parallel()
// t.TempDir paths can exceed the unix socket path limit on macOS.
dir, err := os.MkdirTemp("", "daemonapi")
assert.NilError(t, err)
t.Cleanup(func() { _ = os.RemoveAll(dir) })
sock := filepath.Join(dir, "api.sock")
l, err := net.Listen("unix", sock)
assert.NilError(t, err)
srv := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
// Force a new connection per request so the second Ping dials again.
w.Header().Set("Connection", "close")
}))
_ = srv.Listener.Close()
srv.Listener = l
srv.Start()
t.Cleanup(srv.Close)
api := &fakeAPI{host: "unix://" + sock}
c, err := daemonapiv0.NewClient(api)
assert.NilError(t, err)
t.Cleanup(func() { _ = c.Close() })
assert.Check(t, is.Equal(api.calls.Load(), int32(0)))
for range 2 {
_, err = c.Ping(t.Context(), client.PingOptions{})
assert.NilError(t, err)
}
assert.Check(t, is.Equal(api.calls.Load(), int32(1)))
}