From 6f5ccbb4d894b7520f029a09438b6aed4762408a Mon Sep 17 00:00:00 2001 From: fearful-symmetry Date: Mon, 17 Jun 2024 13:20:53 -0700 Subject: [PATCH 1/5] update IsServerless --- kibana/client.go | 35 ++++++++++++++++++++++++++++++----- kibana/client_test.go | 22 ++++++++++++++++++++++ 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/kibana/client.go b/kibana/client.go index 3292e95e..3ef9c1dd 100644 --- a/kibana/client.go +++ b/kibana/client.go @@ -349,13 +349,38 @@ func (client *Client) GetVersion() version.V { return client.Version } // Right now we don't have an API to tell us if we're running against serverless or not, so this actual implementation is something of a hack. // see https://github.com/elastic/kibana/pull/164850 func (client *Client) KibanaIsServerless() (bool, error) { - ret, _, err := client.Connection.Request("GET", "/api/saved_objects/_find", nil, nil, nil) - if ret > 300 && strings.Contains(err.Error(), "not available with the current configuration") { + + type apiStatus struct { + Version struct { + BuildFlavor string `json:"build_flavor"` + } `json:"version"` + } + + params := http.Header{} + if client.APIKey != "" { + v := "ApiKey " + base64.StdEncoding.EncodeToString([]byte(client.APIKey)) + params.Add("Authorization", v) + } + ret, resp, err := client.Connection.Request("GET", "/api/status", nil, params, nil) + if err != nil { + return false, fmt.Errorf("error in HTTP request: %w", err) + } + respString := string(resp) + if ret > http.StatusMultipleChoices { + return false, fmt.Errorf("got invalid response code: %v (%s)", ret, respString) + } + + status := apiStatus{} + err = json.Unmarshal(resp, &status) + if err != nil { + return false, fmt.Errorf("error unmarshalling JSON: %w", err) + } + if status.Version.BuildFlavor == "serverless" { return true, nil - } else if err != nil { - return false, fmt.Errorf("error checking serverless status: %w", err) + } else { + return false, nil } - return false, nil + } func (client *Client) ImportMultiPartFormFile(url string, params url.Values, filename string, contents string) error { diff --git a/kibana/client_test.go b/kibana/client_test.go index 9d5e14ed..7649f73e 100644 --- a/kibana/client_test.go +++ b/kibana/client_test.go @@ -59,6 +59,28 @@ func assertConnection(t *testing.T, URL string, expectedStatusCode int) { assert.Error(t, err) } +func TestIsServerless(t *testing.T) { + rawStatusCall := `{"name":"kb","uuid":"d2130570-f7d8-463b-bd67-8503150004d5","version":{"number":"8.15.0","build_hash":"13382875e99e8c97f4574d86eca07cac3be9edfc","build_number":75422,"build_snapshot":false,"build_flavor":"stateful","build_date":"2024-06-15T18:13:50.595Z"}}` + + kibanaTS := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write([]byte(rawStatusCall)) + })) + defer kibanaTS.Close() + + conn := Connection{ + URL: kibanaTS.URL, + HTTP: http.DefaultClient, + } + + testClient := Client{ + Connection: conn, + } + + got, err := testClient.KibanaIsServerless() + require.NoError(t, err) + require.False(t, got) +} + func TestErrorBadJson(t *testing.T) { kibanaTS := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusGone) From c63c452a2bca8742db2ed561c2c47d683494f168 Mon Sep 17 00:00:00 2001 From: fearful-symmetry Date: Mon, 17 Jun 2024 13:22:11 -0700 Subject: [PATCH 2/5] cleanup --- kibana/client.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/kibana/client.go b/kibana/client.go index 3ef9c1dd..7b32750f 100644 --- a/kibana/client.go +++ b/kibana/client.go @@ -25,7 +25,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "mime" "mime/multipart" "net/http" @@ -226,7 +225,7 @@ func (conn *Connection) Request(method, extraPath string, } defer resp.Body.Close() - result, err := ioutil.ReadAll(resp.Body) + result, err := io.ReadAll(resp.Body) if err != nil { return 0, nil, fmt.Errorf("fail to read response: %w", err) } @@ -356,15 +355,18 @@ func (client *Client) KibanaIsServerless() (bool, error) { } `json:"version"` } + // we can send a GET to `/api/status` without auth, but it won't actually return version info. params := http.Header{} if client.APIKey != "" { v := "ApiKey " + base64.StdEncoding.EncodeToString([]byte(client.APIKey)) params.Add("Authorization", v) } + ret, resp, err := client.Connection.Request("GET", "/api/status", nil, params, nil) if err != nil { return false, fmt.Errorf("error in HTTP request: %w", err) } + respString := string(resp) if ret > http.StatusMultipleChoices { return false, fmt.Errorf("got invalid response code: %v (%s)", ret, respString) @@ -375,6 +377,7 @@ func (client *Client) KibanaIsServerless() (bool, error) { if err != nil { return false, fmt.Errorf("error unmarshalling JSON: %w", err) } + if status.Version.BuildFlavor == "serverless" { return true, nil } else { From 85ec7096adece193a51e9cc10f1886288423b61a Mon Sep 17 00:00:00 2001 From: fearful-symmetry Date: Mon, 17 Jun 2024 13:27:13 -0700 Subject: [PATCH 3/5] remove old docs --- kibana/client.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/kibana/client.go b/kibana/client.go index 7b32750f..8d3781ff 100644 --- a/kibana/client.go +++ b/kibana/client.go @@ -345,8 +345,6 @@ func (client *Client) readVersion() error { func (client *Client) GetVersion() version.V { return client.Version } // KibanaIsServerless returns true if we're talking to a serverless instance. -// Right now we don't have an API to tell us if we're running against serverless or not, so this actual implementation is something of a hack. -// see https://github.com/elastic/kibana/pull/164850 func (client *Client) KibanaIsServerless() (bool, error) { type apiStatus struct { From f906784e9f74f69cd8eab6218686eb7406e9bb16 Mon Sep 17 00:00:00 2001 From: fearful-symmetry Date: Mon, 17 Jun 2024 13:43:11 -0700 Subject: [PATCH 4/5] fix older test --- transport/httpcommon/diag_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/httpcommon/diag_test.go b/transport/httpcommon/diag_test.go index 4906e3eb..27984bca 100644 --- a/transport/httpcommon/diag_test.go +++ b/transport/httpcommon/diag_test.go @@ -283,7 +283,7 @@ func Test_diagError(t *testing.T) { } _, err = client.Do(req) //nolint:bodyclose // expected to return an error require.Error(t, err) - require.Contains(t, diagError(err), "caused by expired mTLS client cert.") + require.Contains(t, diagError(err), "expired certificate") }) } From 290b1caada972824a6978639faf6269ae32a8542 Mon Sep 17 00:00:00 2001 From: fearful-symmetry Date: Mon, 17 Jun 2024 13:48:43 -0700 Subject: [PATCH 5/5] fixing unrelated tests --- transport/httpcommon/diag_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/transport/httpcommon/diag_test.go b/transport/httpcommon/diag_test.go index 27984bca..767ebe8a 100644 --- a/transport/httpcommon/diag_test.go +++ b/transport/httpcommon/diag_test.go @@ -283,7 +283,8 @@ func Test_diagError(t *testing.T) { } _, err = client.Do(req) //nolint:bodyclose // expected to return an error require.Error(t, err) - require.Contains(t, diagError(err), "expired certificate") + // different OSes seem to report different TLS errors, so just check for the "expired" string. + require.Contains(t, diagError(err), "expired") }) }