From 9feab7946aa0b505aedb9ff31c1716bc9eed3afc Mon Sep 17 00:00:00 2001 From: michel-laterman Date: Mon, 1 Dec 2025 16:18:34 -0800 Subject: [PATCH 1/3] Add kibana func to update download source by id --- kibana/fleet.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/kibana/fleet.go b/kibana/fleet.go index 2c1684b7..e2068815 100644 --- a/kibana/fleet.go +++ b/kibana/fleet.go @@ -45,6 +45,7 @@ const ( fleetUninstallTokensAPI = "/api/fleet/uninstall_tokens" //nolint:gosec // NOT the "Potential hardcoded credentials" fleetUpgradeAgentAPI = "/api/fleet/agents/%s/upgrade" fleetAgentDownloadSourcesAPI = "/api/fleet/agent_download_sources" + fleetAgentDownloadSourceAPI = "/api/fleet/agent_download_sources/%s" fleetProxiesAPI = "/api/fleet/proxies" ) @@ -204,6 +205,51 @@ func (client *Client) CreateDownloadSource(ctx context.Context, source DownloadS return body, nil } +func (client *Client) UpdateDownloadSource(ctx context.Context, id string, source DownloadSource) (DownloadSourceResponse, error) { + reqBody, err := json.Marshal(source) + if err != nil { + return DownloadSourceResponse{}, + fmt.Errorf("unable to marshal DownloadSource into JSON: %w", err) + } + + resp, err := client.SendWithContext( + ctx, + http.MethodPut, + fmt.Sprintf(fleetAgentDownloadSourceAPI, id), + nil, + nil, + bytes.NewReader(reqBody)) + if err != nil { + return DownloadSourceResponse{}, + fmt.Errorf("error calling Agent Binary Download Source API: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + var respBody string + if bs, err := io.ReadAll(resp.Body); err != nil { + respBody = "could not read response body" + } else { + respBody = string(bs) + } + + client.log.Errorw( + "could not update download source, kibana returned "+resp.Status, + "http.response.body.content", respBody) + return DownloadSourceResponse{}, + fmt.Errorf("could not update download source, kibana returned %s. response body: %s: %w", + resp.Status, respBody, err) + } + + body := DownloadSourceResponse{} + if err = json.NewDecoder(resp.Body).Decode(&body); err != nil { + return DownloadSourceResponse{}, + fmt.Errorf("failed parsing download source response: %w", err) + } + + return body, nil +} + // GetPolicy returns the policy with 'policy_id' id. func (client *Client) GetPolicy(ctx context.Context, id string) (r PolicyResponse, err error) { apiURL := fmt.Sprintf(fleetAgentPolicyAPI, id) From 3ceca05882ffbb7e307bb6adc79f0ca33e58b51a Mon Sep 17 00:00:00 2001 From: michel-laterman Date: Tue, 2 Dec 2025 09:04:10 -0800 Subject: [PATCH 2/3] Add unit test --- kibana/fleet_test.go | 45 +++++++++++++++++++ ...fleet_create_download_source_response.json | 8 ++++ ...fleet_update_download_source_response.json | 9 ++++ 3 files changed, 62 insertions(+) create mode 100644 kibana/testdata/fleet_create_download_source_response.json create mode 100644 kibana/testdata/fleet_update_download_source_response.json diff --git a/kibana/fleet_test.go b/kibana/fleet_test.go index 0b60bdfa..603697af 100644 --- a/kibana/fleet_test.go +++ b/kibana/fleet_test.go @@ -54,6 +54,12 @@ var ( //go:embed testdata/fleet_get_fleet_server_host_response.json fleetGetFleetServerHostResponse []byte + + //go:embed testdata/fleet_create_download_source_response.json + fleetCreateDownloadSourceResponse []byte + + //go:embed testdata/fleet_update_download_source_response.json + fleetUpdateDownloadSourceResponse []byte ) func TestFleetCreatePolicy(t *testing.T) { @@ -385,6 +391,45 @@ func TestFleetGetFleetServerHost(t *testing.T) { require.True(t, resp.IsPreconfigured) } +func TestFleetDownloadSource(t *testing.T) { + id := "test-id" + name := "test-name" + handler := func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodPost: + _, _ = w.Write(fleetCreateDownloadSourceResponse) + case http.MethodPut: + _, _ = w.Write(fleetUpdateDownloadSourceResponse) + default: + http.Error(w, http.StatusNotImplemented, "not implemented") + } + } + client, err := createTestServerAndClient(handler) + require.NoError(t, err) + require.NotNil(t, client) + + t.Run("create", func(t *testing.T) { + resp, err := client.CreateDownloadSource(t.Context(), DownloadSource{ + Name: name, + Host: "http://test.local", + }) + require.NoError(t, err) + require.Equal(t, id, resp.Item.ID) + require.Equal(t, name, resp.Item.Name) + require.NotEmpty(t, "http://test.local", resp.Item.Host) + }) + t.Run("update", func(t *testing.T) { + resp, err := client.UpdateDownloadSource(t.Context(), id, DownloadSource{ + Name: name, + Host: "http://newtest.local", + }) + require.NoError(t, err) + require.Equal(t, id, resp.Item.ID) + require.Equal(t, name, resp.Item.Name) + require.NotEmpty(t, "http://newtest.local", resp.Item.Host) + }) +} + func createTestServerAndClient(handler http.HandlerFunc) (*Client, error) { kibanaTS := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { diff --git a/kibana/testdata/fleet_create_download_source_response.json b/kibana/testdata/fleet_create_download_source_response.json new file mode 100644 index 00000000..19924161 --- /dev/null +++ b/kibana/testdata/fleet_create_download_source_response.json @@ -0,0 +1,8 @@ +{ + "item": { + "id": "test-id", + "name": "test-name", + "host": "http://test.local", + "is_default": false + } +} diff --git a/kibana/testdata/fleet_update_download_source_response.json b/kibana/testdata/fleet_update_download_source_response.json new file mode 100644 index 00000000..7305568a --- /dev/null +++ b/kibana/testdata/fleet_update_download_source_response.json @@ -0,0 +1,9 @@ +{ + "item": { + "id": "test-id", + "name": "test-name", + "host": "http://newtest.local", + "is_default": false + } +} + From 3552f067ebd7bc1797940aa81bf33ad9efe80338 Mon Sep 17 00:00:00 2001 From: michel-laterman Date: Tue, 2 Dec 2025 09:37:55 -0800 Subject: [PATCH 3/3] Fix http.Error call --- kibana/fleet_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kibana/fleet_test.go b/kibana/fleet_test.go index 603697af..7f3745a5 100644 --- a/kibana/fleet_test.go +++ b/kibana/fleet_test.go @@ -401,7 +401,7 @@ func TestFleetDownloadSource(t *testing.T) { case http.MethodPut: _, _ = w.Write(fleetUpdateDownloadSourceResponse) default: - http.Error(w, http.StatusNotImplemented, "not implemented") + http.Error(w, "not implemented", http.StatusNotImplemented) } } client, err := createTestServerAndClient(handler)