From a6efc8a5c9a353cfae1b144ed7371f059f610cdd Mon Sep 17 00:00:00 2001 From: Tim Gross Date: Fri, 9 Aug 2024 17:28:04 +0000 Subject: [PATCH] backport of commit b7419bc940151684d19054ee3cef6b732196b50a --- .changelog/23785.txt | 3 +++ api/api.go | 10 +++++++--- api/api_test.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 .changelog/23785.txt diff --git a/.changelog/23785.txt b/.changelog/23785.txt new file mode 100644 index 00000000000..dc15e061bc3 --- /dev/null +++ b/.changelog/23785.txt @@ -0,0 +1,3 @@ +```release-note:bug +api: Fixed a bug where an `api.Config` targeting a unix domain socket could not be reused between clients +``` diff --git a/api/api.go b/api/api.go index 7b42b32de5b..8693167ee4c 100644 --- a/api/api.go +++ b/api/api.go @@ -509,9 +509,13 @@ func NewClient(config *Config) (*Client, error) { } // we have to test the address that comes from DefaultConfig, because it - // could be the value of NOMAD_ADDR which is applied without testing - if config.url, err = url.Parse(config.Address); err != nil { - return nil, fmt.Errorf("invalid address '%s': %v", config.Address, err) + // could be the value of NOMAD_ADDR which is applied without testing. But + // only on the first use of this Config, otherwise we'll have mutated the + // address + if config.url == nil { + if config.url, err = url.Parse(config.Address); err != nil { + return nil, fmt.Errorf("invalid address '%s': %v", config.Address, err) + } } httpClient := config.HttpClient diff --git a/api/api_test.go b/api/api_test.go index 4c62cf073eb..879a81d5787 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -11,9 +11,12 @@ import ( "errors" "fmt" "io" + "net" "net/http" "net/http/httptest" "net/url" + "os" + "path" "strings" "testing" "time" @@ -595,3 +598,44 @@ func TestClient_autoUnzip(t *testing.T) { Body: io.NopCloser(&b), }, nil) } + +func TestUnixSocketConfig(t *testing.T) { + + td := os.TempDir() // testing.TempDir() on macOS makes paths that are too long + socketPath := path.Join(td, t.Name()+".sock") + os.Remove(socketPath) // git rid of stale ones now. + + t.Cleanup(func() { os.Remove(socketPath) }) + + ts := httptest.NewUnstartedServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(`"10.1.1.1"`)) + })) + + // Create a Unix domain socket and listen for incoming connections. + socket, err := net.Listen("unix", socketPath) + must.NoError(t, err) + t.Cleanup(func() { socket.Close() }) + + ts.Listener = socket + ts.Start() + defer ts.Close() + + cfg := &Config{Address: "unix://" + socketPath} + + client1, err := NewClient(cfg) + must.NoError(t, err) + t.Cleanup(client1.Close) + + resp, err := client1.Status().Leader() + must.NoError(t, err) + must.Eq(t, "10.1.1.1", resp) + + client2, err := NewClient(cfg) + must.NoError(t, err) + t.Cleanup(client2.Close) + + _, err = client2.Status().Leader() + must.NoError(t, err) + must.Eq(t, "10.1.1.1", resp) +}