[8.12] [Fleet] Fix 500 in Fleet API when request to product versions endpoint throws ECONNREFUSED (#172850) (#172865)

# Backport

This will backport the following commits from `main` to `8.12`:
- [[Fleet] Fix 500 in Fleet API when request to product versions
endpoint throws ECONNREFUSED
(#172850)](https://github.com/elastic/kibana/pull/172850)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Kyle
Pollich","email":"kyle.pollich@elastic.co"},"sourceCommit":{"committedDate":"2023-12-07T18:14:35Z","message":"[Fleet]
Fix 500 in Fleet API when request to product versions endpoint throws
ECONNREFUSED (#172850)\n\n## Summary\r\n\r\nNetwork-level errors will
cause `fetch` to `throw` rather than resolving\r\nwith a status code.
This PR updates our logic to handle this case for\r\nairgapped
environments where `ECONNREFUSED` style errors squash HTTP\r\nrequests
at the DNS
level.","sha":"be6fbc4dcc8fff7e7419cf3fa9b05a6b13e3edba","branchLabelMapping":{"^v8.13.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","Team:Fleet","backport:prev-minor","v8.12.0","v8.13.0","v8.11.3"],"number":172850,"url":"https://github.com/elastic/kibana/pull/172850","mergeCommit":{"message":"[Fleet]
Fix 500 in Fleet API when request to product versions endpoint throws
ECONNREFUSED (#172850)\n\n## Summary\r\n\r\nNetwork-level errors will
cause `fetch` to `throw` rather than resolving\r\nwith a status code.
This PR updates our logic to handle this case for\r\nairgapped
environments where `ECONNREFUSED` style errors squash HTTP\r\nrequests
at the DNS
level.","sha":"be6fbc4dcc8fff7e7419cf3fa9b05a6b13e3edba"}},"sourceBranch":"main","suggestedTargetBranches":["8.12","8.11"],"targetPullRequestStates":[{"branch":"8.12","label":"v8.12.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.13.0","labelRegex":"^v8.13.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/172850","number":172850,"mergeCommit":{"message":"[Fleet]
Fix 500 in Fleet API when request to product versions endpoint throws
ECONNREFUSED (#172850)\n\n## Summary\r\n\r\nNetwork-level errors will
cause `fetch` to `throw` rather than resolving\r\nwith a status code.
This PR updates our logic to handle this case for\r\nairgapped
environments where `ECONNREFUSED` style errors squash HTTP\r\nrequests
at the DNS
level.","sha":"be6fbc4dcc8fff7e7419cf3fa9b05a6b13e3edba"}},{"branch":"8.11","label":"v8.11.3","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Kyle Pollich <kyle.pollich@elastic.co>
This commit is contained in:
Kibana Machine 2023-12-07 14:28:45 -05:00 committed by GitHub
parent e5bcd5f9a6
commit c360cc9cb7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 48 additions and 14 deletions

View file

@ -179,4 +179,29 @@ describe('getAvailableVersions', () => {
expect(mockedFetch).toBeCalledTimes(1);
expect(res2).not.toContain('300.0.0');
});
it('should gracefully handle 400 errors when fetching from product versions API', async () => {
mockKibanaVersion = '300.0.0';
mockedReadFile.mockResolvedValue(`["8.1.0", "8.0.0", "7.17.0", "7.16.0"]`);
mockedFetch.mockResolvedValue({
status: 400,
text: 'Bad request',
} as any);
const res = await getAvailableVersions({ ignoreCache: true });
// Should sort, uniquify and filter out versions < 7.17
expect(res).toEqual(['8.1.0', '8.0.0', '7.17.0']);
});
it('should gracefully handle network errors when fetching from product versions API', async () => {
mockKibanaVersion = '300.0.0';
mockedReadFile.mockResolvedValue(`["8.1.0", "8.0.0", "7.17.0", "7.16.0"]`);
mockedFetch.mockRejectedValue('ECONNREFUSED');
const res = await getAvailableVersions({ ignoreCache: true });
// Should sort, uniquify and filter out versions < 7.17
expect(res).toEqual(['8.1.0', '8.0.0', '7.17.0']);
});
});

View file

@ -24,6 +24,7 @@ const AGENT_VERSION_BUILD_FILE = 'x-pack/plugins/fleet/target/agent_versions_lis
// Endpoint maintained by the web-team and hosted on the elastic website
const PRODUCT_VERSIONS_URL = 'https://www.elastic.co/api/product_versions';
const MAX_REQUEST_TIMEOUT = 60 * 1000; // Only attempt to fetch product versions for one minute total
// Cache available versions in memory for 1 hour
const CACHE_DURATION = 1000 * 60 * 60;
@ -118,21 +119,29 @@ async function fetchAgentVersionsFromApi() {
},
};
const response = await pRetry(() => fetch(PRODUCT_VERSIONS_URL, options), { retries: 1 });
const rawBody = await response.text();
try {
const response = await pRetry(() => fetch(PRODUCT_VERSIONS_URL, options), {
retries: 1,
maxRetryTime: MAX_REQUEST_TIMEOUT,
});
const rawBody = await response.text();
// We need to handle non-200 responses gracefully here to support airgapped environments where
// Kibana doesn't have internet access to query this API
if (response.status >= 400) {
logger.debug(`Status code ${response.status} received from versions API: ${rawBody}`);
// We need to handle non-200 responses gracefully here to support airgapped environments where
// Kibana doesn't have internet access to query this API
if (response.status >= 400) {
logger.debug(`Status code ${response.status} received from versions API: ${rawBody}`);
return [];
}
const jsonBody = JSON.parse(rawBody);
const versions: string[] = (jsonBody.length ? jsonBody[0] : [])
.filter((item: any) => item?.title?.includes('Elastic Agent'))
.map((item: any) => item?.version_number);
return versions;
} catch (error) {
logger.debug(`Error fetching available versions from API: ${error.message}`);
return [];
}
const jsonBody = JSON.parse(rawBody);
const versions: string[] = (jsonBody.length ? jsonBody[0] : [])
.filter((item: any) => item?.title?.includes('Elastic Agent'))
.map((item: any) => item?.version_number);
return versions;
}