Skip to content

Commit

Permalink
[Fleet] changed diagnostics query to speed up (elastic#149812)
Browse files Browse the repository at this point in the history
## Summary

Changed query of diagnostics files to speed up seeing the files. This is
because the agent has a delay of about 4m to ack the action, this has to
be fixed separately, see here
elastic/elastic-agent#1703 (comment)

Related to elastic#141074

We can search for the diagnostics file by `agent_id` and `action_id`, so
don't have to wait for the `upload_id` which comes from
`.fleet-actions-results`.


https://user-images.githubusercontent.com/90178898/215451881-bfaa9e86-e055-4490-87b1-dc1d1076a738.mov

Displaying error from agent when diagnostics failed:

<img width="839" alt="image"
src="https://user-images.githubusercontent.com/90178898/215476207-5db7e935-28dd-432e-a6a6-195da162028a.png">


E.g. `.fleet-files-agent`

```
{
        "_index": ".fleet-files-agent-000001",
        "_id": "8a004559-0731-4b8f-b29e-d7405ca0d68c.3a1f21b3-4559-4d3f-aae0-58356c269a92",
        "_score": null,
        "_source": {
          "action_id": "8a004559-0731-4b8f-b29e-d7405ca0d68c",
          "agent_id": "3a1f21b3-4559-4d3f-aae0-58356c269a92",
          "contents": null,
          "file": {
            "ChunkSize": 4194304,
            "Status": "READY",
            "ext": "zip",
            "hash": {
              "md5": "",
              "sha256": ""
            },
            "mime_type": "application/zip",
            "name": "elastic-agent-diagnostics-2023-01-30T10-13-33Z-00.zip",
            "size": 577178
          },
          "src": "agent",
          "upload_id": "988da8ad-9d92-4d18-b5b0-b2a7e77f5a81",
          "upload_start": 1675073615066,
          "transithash": {
            "sha256": "8a417cc8a73e32723ff449b603412113f319c7447044e81acab3f57d4e8226c8"
          }
        },
```

Changed the style to be more consistent:

<img width="898" alt="image"
src="https://user-images.githubusercontent.com/90178898/215492173-7362fab7-15e6-4de9-824b-239164512231.png">



### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
  • Loading branch information
juliaElastic authored and kqualters-elastic committed Feb 6, 2023
1 parent 2abe68f commit 82ef3e1
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 22 deletions.
1 change: 1 addition & 0 deletions x-pack/plugins/fleet/common/types/models/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ export interface AgentDiagnostics {
filePath: string;
status: 'READY' | 'AWAITING_UPLOAD' | 'DELETED' | 'IN_PROGRESS' | 'FAILED';
actionId: string;
error?: string;
}

// Generated from FleetServer schema.json
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ const FlexStartEuiFlexItem = styled(EuiFlexItem)`
align-self: flex-start;
`;

const MarginedIcon = styled(EuiIcon)`
margin-right: 7px;
`;

export interface AgentDiagnosticsProps {
agent: Agent;
}
Expand All @@ -48,6 +52,7 @@ export const AgentDiagnosticsTab: React.FunctionComponent<AgentDiagnosticsProps>
const [isLoading, setIsLoading] = useState(true);
const [diagnosticsEntries, setDiagnosticEntries] = useState<AgentDiagnostics[]>([]);
const [prevDiagnosticsEntries, setPrevDiagnosticEntries] = useState<AgentDiagnostics[]>([]);
const [loadInterval, setLoadInterval] = useState(10000);

const loadData = useCallback(async () => {
try {
Expand All @@ -59,8 +64,20 @@ export const AgentDiagnosticsTab: React.FunctionComponent<AgentDiagnosticsProps>
if (!uploadsResponse.data) {
throw new Error('No data');
}
setDiagnosticEntries(uploadsResponse.data.items);
const entries = uploadsResponse.data.items;
setDiagnosticEntries(entries);
setIsLoading(false);

// query faster if an action is in progress, for quicker feedback
if (
entries.some(
(entry) => entry.status === 'IN_PROGRESS' || entry.status === 'AWAITING_UPLOAD'
)
) {
setLoadInterval(3000);
} else {
setLoadInterval(10000);
}
} catch (err) {
notifications.toasts.addError(err, {
title: i18n.translate(
Expand All @@ -71,13 +88,13 @@ export const AgentDiagnosticsTab: React.FunctionComponent<AgentDiagnosticsProps>
),
});
}
}, [agent.id, notifications.toasts]);
}, [agent.id, notifications.toasts, setLoadInterval]);

useEffect(() => {
loadData();
const interval: ReturnType<typeof setInterval> | null = setInterval(async () => {
loadData();
}, 10000);
}, loadInterval);

const cleanup = () => {
if (interval) {
Expand All @@ -86,7 +103,7 @@ export const AgentDiagnosticsTab: React.FunctionComponent<AgentDiagnosticsProps>
};

return cleanup;
}, [loadData]);
}, [loadData, loadInterval]);

useEffect(() => {
setPrevDiagnosticEntries(diagnosticsEntries);
Expand All @@ -112,6 +129,9 @@ export const AgentDiagnosticsTab: React.FunctionComponent<AgentDiagnosticsProps>
}
}, [prevDiagnosticsEntries, diagnosticsEntries, notifications.toasts]);

const errorIcon = <MarginedIcon type="alert" color="red" />;
const getErrorMessage = (error?: string) => (error ? `Error: ${error}` : '');

const columns: Array<EuiTableFieldDataColumnType<AgentDiagnostics>> = [
{
field: 'id',
Expand All @@ -123,21 +143,32 @@ export const AgentDiagnosticsTab: React.FunctionComponent<AgentDiagnosticsProps>
<EuiIcon type="download" /> &nbsp; {currentItem?.name}
</EuiLink>
) : currentItem?.status === 'IN_PROGRESS' || currentItem?.status === 'AWAITING_UPLOAD' ? (
<EuiText color="subdued">
<EuiLink color="subdued" disabled>
<EuiLoadingSpinner /> &nbsp;
<FormattedMessage
id="xpack.fleet.requestDiagnostics.generatingText"
defaultMessage="Generating diagnostics file..."
/>
</EuiText>
</EuiLink>
) : (
<EuiText color="subdued">
<EuiToolTip content={`Diagnostics status: ${currentItem?.status}`}>
<EuiIcon type="alert" color="red" />
</EuiToolTip>
<EuiLink color="subdued" disabled>
{currentItem?.status ? (
<EuiToolTip
content={
<>
<p>Diagnostics status: {currentItem?.status}</p>
<p>{getErrorMessage(currentItem?.error)}</p>
</>
}
>
{errorIcon}
</EuiToolTip>
) : (
errorIcon
)}
&nbsp;
{currentItem?.name}
</EuiText>
</EuiLink>
);
},
},
Expand All @@ -149,7 +180,7 @@ export const AgentDiagnosticsTab: React.FunctionComponent<AgentDiagnosticsProps>
const currentItem = diagnosticsEntries.find((item) => item.id === id);
return (
<EuiText color={currentItem?.status === 'READY' ? 'default' : 'subdued'}>
{formatDate(currentItem?.createTime, 'll')}
{formatDate(currentItem?.createTime, 'lll')}
</EuiText>
);
},
Expand All @@ -171,6 +202,7 @@ export const AgentDiagnosticsTab: React.FunctionComponent<AgentDiagnosticsProps>
}
);
notifications.toasts.addSuccess(successMessage);
loadData();
} catch (error) {
setIsSubmitting(false);
notifications.toasts.addError(error, {
Expand Down
27 changes: 18 additions & 9 deletions x-pack/plugins/fleet/server/services/agents/uploads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,24 @@ export async function getAgentUploads(
esClient: ElasticsearchClient,
agentId: string
): Promise<AgentDiagnostics[]> {
const getFile = async (fileId: string) => {
if (!fileId) return;
const getFile = async (actionId: string) => {
try {
const fileResponse = await esClient.search({
index: FILE_STORAGE_METADATA_AGENT_INDEX,
query: {
bool: {
filter: {
term: { upload_id: fileId },
bool: {
must: [{ term: { agent_id: agentId } }, { term: { action_id: actionId } }],
},
},
},
},
});
if (fileResponse.hits.total === 0) {
appContextService.getLogger().debug(`No matches for upload_id ${fileId}`);
if (fileResponse.hits.hits.length === 0) {
appContextService
.getLogger()
.debug(`No matches for action_id ${actionId} and agent_id ${agentId}`);
return;
}
return {
Expand All @@ -64,10 +67,14 @@ export async function getAgentUploads(

const actions = await _getRequestDiagnosticsActions(esClient, agentId);

const results = [];
const results: AgentDiagnostics[] = [];
for (const action of actions) {
const file = action.fileId ? await getFile(action.fileId) : undefined;
const fileName = file?.name ?? `${moment(action.timestamp!).format('YYYY-MM-DD HH:mm:ss')}.zip`;
const file = await getFile(action.actionId);
const fileName =
file?.name ??
`elastic-agent-diagnostics-${moment
.utc(action.timestamp!)
.format('YYYY-MM-DDTHH-mm-ss')}Z-00.zip`;
const filePath = file ? agentRouteService.getAgentFileDownloadLink(file.id, file.name) : '';
const result = {
actionId: action.actionId,
Expand All @@ -76,6 +83,7 @@ export async function getAgentUploads(
name: fileName,
createTime: action.timestamp!,
filePath,
error: action.error,
};
results.push(result);
}
Expand All @@ -91,6 +99,7 @@ async function _getRequestDiagnosticsActions(
index: AGENT_ACTIONS_INDEX,
ignore_unavailable: true,
size: SO_SEARCH_LIMIT,
sort: { '@timestamp': 'desc' },
query: {
bool: {
must: [
Expand Down Expand Up @@ -150,7 +159,7 @@ async function _getRequestDiagnosticsActions(
const actionResult = actionResults.find((result) => result.actionId === action.actionId);
return {
actionId: action.actionId,
timestamp: actionResult?.timestamp ?? action.timestamp,
timestamp: action.timestamp,
fileId: actionResult?.fileId,
error: actionResult?.error,
};
Expand Down
48 changes: 47 additions & 1 deletion x-pack/test/fleet_api_integration/apis/agents/uploads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ export default function (providerContext: FtrProviderContext) {
doc_as_upsert: true,
doc: {
upload_id: 'file1',
action_id: 'action1',
agent_id: 'agent1',
file: {
ChunkSize: 4194304,
extension: 'zip',
Expand Down Expand Up @@ -96,7 +98,7 @@ export default function (providerContext: FtrProviderContext) {

expect(body.items[0]).to.eql({
actionId: 'action1',
createTime: '2022-10-07T12:00:00.000Z',
createTime: '2022-10-07T11:00:00.000Z',
filePath:
'/api/fleet/agents/files/file1/elastic-agent-diagnostics-2022-10-07T12-00-00Z-00.zip',
id: 'file1',
Expand Down Expand Up @@ -130,5 +132,49 @@ export default function (providerContext: FtrProviderContext) {
'attachment; filename="elastic-agent-diagnostics-2022-10-07T12-00-00Z-00.zip"'
);
});

it('should return failed status with error message', async () => {
await esClient.create({
index: AGENT_ACTIONS_INDEX,
id: new Date().toISOString(),
refresh: true,
body: {
type: 'REQUEST_DIAGNOSTICS',
action_id: 'action2',
agents: ['agent2'],
'@timestamp': '2022-10-07T11:00:00.000Z',
},
});
await esClient.create(
{
index: AGENT_ACTIONS_RESULTS_INDEX,
id: new Date().toISOString(),
refresh: true,
body: {
action_id: 'action2',
agent_id: 'agent2',
'@timestamp': '2022-10-07T12:00:00.000Z',
data: {},
error: 'rate limit exceeded',
},
},
ES_INDEX_OPTIONS
);

const { body } = await supertest
.get(`/api/fleet/agents/agent2/uploads`)
.set('kbn-xsrf', 'xxx')
.expect(200);

expect(body.items[0]).to.eql({
actionId: 'action2',
createTime: '2022-10-07T11:00:00.000Z',
filePath: '',
id: 'action2',
name: 'elastic-agent-diagnostics-2022-10-07T11-00-00Z-00.zip',
status: 'FAILED',
error: 'rate limit exceeded',
});
});
});
}

0 comments on commit 82ef3e1

Please sign in to comment.