Skip to content

Commit

Permalink
[Fleet] Replace Console API with internal API (elastic#186899)
Browse files Browse the repository at this point in the history
## Summary

Closes elastic#160194

Parts of the [Fleet
debugger](https://github.com/elastic/kibana/blob/main/x-pack/plugins/fleet/dev_docs/fleet_debugger.md)
currently use the Console API to query Fleet indices and Ingest Saved
Objects. This PR replaces those uses with internal API endpoints that
use Elasticsearch and Saved Objects clients.

Affected parts:
- Fleet Index Debugger:
   - This component directly queries some Fleet indices.
   - The new service uses an Elasticsearch client.
- There should be no difference with respect to the previous behaviour.
- Saved Object Debugger:
- This component directly queries the `.kibana-ingest` index to retrieve
saved objects of a given type.
- Based on the type, the existing saved object names are retrieved: this
is now done using a Saved Objects client and should work the same.
- When a name is selected, the actual saved object is fetched: this is
now done using a Saved Objects client and there are small differences in
the result (no information is lost).

The following example shows the difference between the saved object
response before and after this change:
<details>
<summary>Before</summary>
  
```json
{
  "total": {
    "value": 1,
    "relation": "eq"
  },
  "max_score": 0.9808291,
  "hits": [
    {
      "_index": ".kibana_ingest_8.15.0_001",
      "_id": "ingest-agent-policies:174cc817-06d7-4e2c-88b3-44d7cb5322b6",
      "_score": 0.9808291,
      "_source": {
        "ingest-agent-policies": {
          "name": "Agent policy 1",
          "description": "",
          "namespace": "default",
          "monitoring_enabled": [
            "logs",
            "metrics"
          ],
          "inactivity_timeout": 1209600,
          "is_protected": false,
          "status": "active",
          "is_managed": false,
          "revision": 1,
          "updated_at": "2024-06-24T08:15:37.813Z",
          "updated_by": "elastic",
          "schema_version": "1.1.1"
        },
        "type": "ingest-agent-policies",
        "references": [],
        "managed": false,
        "coreMigrationVersion": "8.8.0",
        "typeMigrationVersion": "10.3.0",
        "updated_at": "2024-06-24T08:15:37.813Z",
        "created_at": "2024-06-24T08:15:37.813Z"
      }
    }
  ]
}
```
</details>

<details>
<summary>After</summary>
  
```json
[
  {
    "type": "ingest-agent-policies",
    "id": "174cc817-06d7-4e2c-88b3-44d7cb5322b6",
    "attributes": {
      "name": "Agent policy 1",
      "description": "",
      "namespace": "default",
      "monitoring_enabled": [
        "logs",
        "metrics"
      ],
      "inactivity_timeout": 1209600,
      "is_protected": false,
      "status": "active",
      "is_managed": false,
      "revision": 1,
      "updated_at": "2024-06-24T08:15:37.813Z",
      "updated_by": "elastic",
      "schema_version": "1.1.1"
    },
    "references": [],
    "managed": false,
    "updated_at": "2024-06-24T08:15:37.813Z",
    "created_at": "2024-06-24T08:15:37.813Z",
    "version": "WzQ2OCwxXQ==",
    "coreMigrationVersion": "8.8.0",
    "typeMigrationVersion": "10.3.0",
    "score": 0.9808291
  }
]
```
</details>

### Testing

Go to `app/fleet/_debug` and check the various parts described above.

### Screenshots

<img width="1918" alt="Screenshot 2024-06-26 at 15 28 07"
src="https://github.com/elastic/kibana/assets/23701614/8541e4e6-1f02-4a31-936b-e5e24dd1994c">

<img width="1918" alt="Screenshot 2024-06-26 at 15 27 52"
src="https://github.com/elastic/kibana/assets/23701614/80d0e041-115f-42b5-95ab-cf2a601b74c8">

### Checklist

- [ ] [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
jillguyonnet authored Jun 27, 2024
1 parent 31e132b commit 028994c
Show file tree
Hide file tree
Showing 11 changed files with 228 additions and 56 deletions.
8 changes: 8 additions & 0 deletions x-pack/plugins/fleet/common/constants/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,14 @@ export const DOWNLOAD_SOURCE_API_ROUTES = {
DELETE_PATTERN: `${API_ROOT}/agent_download_sources/{sourceId}`,
};

// Fleet debug routes

export const FLEET_DEBUG_ROUTES = {
INDEX_PATTERN: `${INTERNAL_ROOT}/debug/index`,
SAVED_OBJECTS_PATTERN: `${INTERNAL_ROOT}/debug/saved_objects`,
SAVED_OBJECT_NAMES_PATTERN: `${INTERNAL_ROOT}/debug/saved_object_names`,
};

// API versioning constants
export const API_VERSIONS = {
public: {
Expand Down
7 changes: 7 additions & 0 deletions x-pack/plugins/fleet/common/services/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
FLEET_SERVER_HOST_API_ROUTES,
FLEET_PROXY_API_ROUTES,
UNINSTALL_TOKEN_ROUTES,
FLEET_DEBUG_ROUTES,
} from '../constants';

export const epmRouteService = {
Expand Down Expand Up @@ -322,3 +323,9 @@ export const downloadSourceRoutesService = {
DOWNLOAD_SOURCE_API_ROUTES.DELETE_PATTERN.replace('{sourceId}', downloadSourceId),
getCreatePath: () => DOWNLOAD_SOURCE_API_ROUTES.CREATE_PATTERN,
};

export const debugRoutesService = {
getIndexPath: () => FLEET_DEBUG_ROUTES.INDEX_PATTERN,
getSavedObjectsPath: () => FLEET_DEBUG_ROUTES.SAVED_OBJECTS_PATTERN,
getSavedObjectNamesPath: () => FLEET_DEBUG_ROUTES.SAVED_OBJECT_NAMES_PATTERN,
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,33 @@ import { useQuery } from '@tanstack/react-query';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';

import { AGENTS_INDEX, AGENT_ACTIONS_INDEX, API_VERSIONS } from '../../../../../../common';

import { sendRequest } from '../../../hooks';

import { debugRoutesService } from '../../../../../../common/services';

import { ENROLLMENT_API_KEYS_INDEX } from '../../../constants';

import { CodeBlock } from './code_block';

const fetchIndex = async (index?: string) => {
if (!index) return;
const path = `/${index}/_search`;
const response = await sendRequest({
method: 'post',
path: `/api/console/proxy`,
query: {
path,
method: 'GET',
},
path: debugRoutesService.getIndexPath(),
body: { index },
version: API_VERSIONS.internal.v1,
});

return response;
};

export const FleetIndexDebugger = () => {
const indices = [
{ label: '.fleet-agents', value: '.fleet-agents' },
{ label: '.fleet-actions', value: '.fleet-actions' },
{ label: '.fleet-enrollment-api-keys', value: '.fleet-enrollment-api-keys' },
{ label: AGENTS_INDEX, value: AGENTS_INDEX },
{ label: AGENT_ACTIONS_INDEX, value: AGENT_ACTIONS_INDEX },
{ label: ENROLLMENT_API_KEYS_INDEX, value: ENROLLMENT_API_KEYS_INDEX },
];
const [index, setIndex] = useState<string | undefined>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,50 +22,39 @@ import { FormattedMessage } from '@kbn/i18n-react';

import { sendRequest } from '../../../hooks';

import { debugRoutesService } from '../../../../../../common/services';

import {
OUTPUT_SAVED_OBJECT_TYPE,
AGENT_POLICY_SAVED_OBJECT_TYPE,
PACKAGE_POLICY_SAVED_OBJECT_TYPE,
PACKAGES_SAVED_OBJECT_TYPE,
DOWNLOAD_SOURCE_SAVED_OBJECT_TYPE,
FLEET_SERVER_HOST_SAVED_OBJECT_TYPE,
INGEST_SAVED_OBJECT_INDEX,
API_VERSIONS,
} from '../../../../../../common/constants';

import { CodeBlock } from './code_block';
import { SavedObjectNamesCombo } from './saved_object_names_combo';

const fetchSavedObjects = async (type?: string, name?: string) => {
if (!type || !name) return;
const path = `/${INGEST_SAVED_OBJECT_INDEX}/_search`;
const body = {
query: {
bool: {
must: {
match: { [`${type}.name`]: name },
},
filter: {
term: {
type,
},
},
},
},
};

const response = await sendRequest({
method: 'post',
path: `/api/console/proxy`,
query: {
path,
method: 'GET',
path: debugRoutesService.getSavedObjectsPath(),
body: {
type,
name,
},
body,
version: API_VERSIONS.internal.v1,
});

if (response.error) {
throw new Error(response.error.message);
}
return response.data?.hits;

return response.data?.saved_objects;
};

export const SavedObjectDebugger: React.FunctionComponent = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,15 @@ import { EuiComboBox } from '@elastic/eui';
import { i18n } from '@kbn/i18n';

import { sendRequest } from '../../../hooks';
import { INGEST_SAVED_OBJECT_INDEX } from '../../../../../../common/constants';
import { debugRoutesService } from '../../../../../../common/services';
import { API_VERSIONS } from '../../../../../../common/constants';

const fetchSavedObjectNames = async (type: string) => {
const path = `/${INGEST_SAVED_OBJECT_INDEX}/_search`;
const body = {
size: 0,
query: {
bool: {
filter: {
term: {
type,
},
},
},
},
aggs: {
names: {
terms: { field: `${type}.name`, size: 500 },
},
},
};
const response = await sendRequest({
method: 'post',
path: `/api/console/proxy`,
query: {
path,
method: 'GET',
},
body,
path: debugRoutesService.getSavedObjectNamesPath(),
body: { type },
version: API_VERSIONS.internal.v1,
});

if (response.error) {
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/fleet/server/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export {
PRECONFIGURATION_API_ROUTES,
DOWNLOAD_SOURCE_API_ROOT,
DOWNLOAD_SOURCE_API_ROUTES,
FLEET_DEBUG_ROUTES,
// Saved Object indices
INGEST_SAVED_OBJECT_INDEX,
// Saved object types
Expand Down
47 changes: 47 additions & 0 deletions x-pack/plugins/fleet/server/routes/debug/handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import type { TypeOf } from '@kbn/config-schema';

import type { FleetRequestHandler } from '../../types';
import { fetchIndex, fetchSavedObjectNames, fetchSavedObjects } from '../../services/debug';
import type {
FetchIndexRequestSchema,
FetchSavedObjectNamesRequestSchema,
FetchSavedObjectsRequestSchema,
} from '../../types/rest_spec/debug';

export const fetchIndexHandler: FleetRequestHandler<
undefined,
undefined,
TypeOf<typeof FetchIndexRequestSchema.body>
> = async (context, request, response) => {
const coreContext = await context.core;
const esClient = coreContext.elasticsearch.client.asInternalUser;
const res = await fetchIndex(esClient, request.body.index);
return response.ok({ body: res });
};

export const fetchSavedObjectsHandler: FleetRequestHandler<
undefined,
undefined,
TypeOf<typeof FetchSavedObjectsRequestSchema.body>
> = async (context, request, response) => {
const soClient = (await context.fleet).internalSoClient;
const res = await fetchSavedObjects(soClient, request.body.type, request.body.name);
return response.ok({ body: res });
};

export const fetchSavedObjectNamesHandler: FleetRequestHandler<
undefined,
undefined,
TypeOf<typeof FetchSavedObjectNamesRequestSchema.body>
> = async (context, request, response) => {
const soClient = (await context.fleet).internalSoClient;
const res = await fetchSavedObjectNames(soClient, request.body.type);
return response.ok({ body: res });
};
73 changes: 73 additions & 0 deletions x-pack/plugins/fleet/server/routes/debug/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import type { FleetAuthzRouter } from '../../services/security';

import { FLEET_DEBUG_ROUTES } from '../../constants';
import { API_VERSIONS } from '../../../common/constants';

import {
FetchIndexRequestSchema,
FetchSavedObjectNamesRequestSchema,
FetchSavedObjectsRequestSchema,
} from '../../types/rest_spec/debug';

import {
fetchIndexHandler,
fetchSavedObjectNamesHandler,
fetchSavedObjectsHandler,
} from './handler';

export const registerRoutes = (router: FleetAuthzRouter) => {
router.versioned
.post({
path: FLEET_DEBUG_ROUTES.INDEX_PATTERN,
access: 'internal',
fleetAuthz: {
fleet: { all: true },
},
})
.addVersion(
{
version: API_VERSIONS.internal.v1,
validate: { request: FetchIndexRequestSchema },
},
fetchIndexHandler
);

router.versioned
.post({
path: FLEET_DEBUG_ROUTES.SAVED_OBJECTS_PATTERN,
access: 'internal',
fleetAuthz: {
fleet: { all: true },
},
})
.addVersion(
{
version: API_VERSIONS.internal.v1,
validate: { request: FetchSavedObjectsRequestSchema },
},
fetchSavedObjectsHandler
);

router.versioned
.post({
path: FLEET_DEBUG_ROUTES.SAVED_OBJECT_NAMES_PATTERN,
access: 'internal',
fleetAuthz: {
fleet: { all: true },
},
})
.addVersion(
{
version: API_VERSIONS.internal.v1,
validate: { request: FetchSavedObjectNamesRequestSchema },
},
fetchSavedObjectNamesHandler
);
};
2 changes: 2 additions & 0 deletions x-pack/plugins/fleet/server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { registerRoutes as registerFleetServerHostRoutes } from './fleet_server_
import { registerRoutes as registerFleetProxiesRoutes } from './fleet_proxies';
import { registerRoutes as registerMessageSigningServiceRoutes } from './message_signing_service';
import { registerRoutes as registerUninstallTokenRoutes } from './uninstall_token';
import { registerRoutes as registerDebugRoutes } from './debug';

export function registerRoutes(fleetAuthzRouter: FleetAuthzRouter, config: FleetConfigType) {
// Always register app routes for permissions checking
Expand All @@ -47,6 +48,7 @@ export function registerRoutes(fleetAuthzRouter: FleetAuthzRouter, config: Fleet
registerHealthCheckRoutes(fleetAuthzRouter);
registerMessageSigningServiceRoutes(fleetAuthzRouter);
registerUninstallTokenRoutes(fleetAuthzRouter, config);
registerDebugRoutes(fleetAuthzRouter);

// Conditional config routes
if (config.agents.enabled) {
Expand Down
35 changes: 35 additions & 0 deletions x-pack/plugins/fleet/server/services/debug/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server';

export async function fetchIndex(esClient: ElasticsearchClient, index: string) {
return esClient.search({ index });
}

export async function fetchSavedObjects(
soClient: SavedObjectsClientContract,
type: string,
name: string
) {
return soClient.find({
type,
search: `\"${name}\"`, // Search for phrase
searchFields: ['name'], // SO type automatically inferred
});
}

export async function fetchSavedObjectNames(soClient: SavedObjectsClientContract, type: string) {
return soClient.find({
type,
aggs: {
names: {
terms: { field: `${type}.attributes.name` }, // cf. SavedObjectsFindOptions definition in packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts
},
},
});
}
27 changes: 27 additions & 0 deletions x-pack/plugins/fleet/server/types/rest_spec/debug.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { schema } from '@kbn/config-schema';

export const FetchIndexRequestSchema = {
body: schema.object({
index: schema.string(),
}),
};

export const FetchSavedObjectsRequestSchema = {
body: schema.object({
type: schema.string(),
name: schema.string(),
}),
};

export const FetchSavedObjectNamesRequestSchema = {
body: schema.object({
type: schema.string(),
}),
};

0 comments on commit 028994c

Please sign in to comment.