Skip to content

Commit

Permalink
[Fleet] use @kbn/config-schema in Fleet API (Part 1) (elastic#192447)
Browse files Browse the repository at this point in the history
## Summary

Part of elastic#184685

First set of changes, I'm planning to do more prs for the remaining
endpoints.

Will also look at making sure these schema definitions are included in
the final kibana `bundle.json`, related doc:
https://elasticco.atlassian.net/wiki/spaces/DOC/pages/450494532/API+reference+docs

When all schema definitions are moved to code, the fleet/openapi folder
can be deleted.

To check the result, add to `kibana.dev.yml`: `server.oas.enabled: true`
And then in kibana console, query:
```
GET kbn:/api/oas?pathStartsWith=/api/fleet/setup
GET kbn:/api/oas?pathStartsWith=/api/fleet/agents/setup

GET kbn:/api/oas?pathStartsWith=/api/fleet/package_policies
GET kbn:/api/oas?pathStartsWith=/api/fleet/package_policies/delete
GET kbn:/api/oas?pathStartsWith=/api/fleet/settings

GET kbn:/api/oas?pathStartsWith=/internal/fleet/settings/enrollment
```

To generate the bundle from code, run:
```
node scripts/capture_oas_snapshot --include-path /api/fleet --no-serverless --update

# writes to oas_docs/bundle.json
```

Response:
```
{
  "openapi": "3.0.0",
  "info": {
    "title": "Kibana HTTP APIs",
    "version": "0.0.0"
  },
  "servers": [
    {
      "url": "http://localhost:5603/julia"
    }
  ],
  "paths": {
    "/api/fleet/setup": {
      "post": {
        "summary": "",
        "tags": [],
        "description": "Initiate Fleet setup",
        "responses": {
          "200": {
            "content": {
              "application/json; Elastic-Api-Version=2023-10-31": {
                "schema": {
                  "type": "object",
                  "description": "A summary of the result of Fleet's `setup` lifecycle. If `isInitialized` is true, Fleet is ready to accept agent enrollment. `nonFatalErrors` may include useful insight into non-blocking issues with Fleet setup.",
                  "properties": {
                    "isInitialized": {
                      "type": "boolean"
                    },
                    "nonFatalErrors": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "message": {
                            "type": "string"
                          }
                        },
                        "additionalProperties": false,
                        "required": [
                          "name",
                          "message"
                        ]
                      }
                    }
                  },
                  "additionalProperties": false,
                  "required": [
                    "isInitialized",
                    "nonFatalErrors"
                  ]
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json; Elastic-Api-Version=2023-10-31": {
                "schema": {
                  "type": "object",
                  "description": "Generic Error",
                  "properties": {
                    "statusCode": {
                      "type": "number"
                    },
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  },
                  "additionalProperties": false,
                  "required": [
                    "statusCode",
                    "error",
                    "message"
                  ]
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json; Elastic-Api-Version=2023-10-31": {
                "schema": {
                  "type": "object",
                  "description": "Internal Server Error",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  },
                  "additionalProperties": false,
                  "required": [
                    "message"
                  ]
                }
              }
            }
          }
        },
        "parameters": [
          {
            "in": "header",
            "name": "elastic-api-version",
            "description": "The version of the API to use",
            "schema": {
              "type": "string",
              "enum": [
                "2023-10-31"
              ],
              "default": "2023-10-31"
            }
          },
          {
            "description": "A required header to protect against CSRF attacks",
            "in": "header",
            "name": "kbn-xsrf",
            "required": true,
            "schema": {
              "example": "true",
              "type": "string"
            }
          }
        ],
        "operationId": "%2Fapi%2Ffleet%2Fsetup#0"
      }
    }
  },
  "components": {
    "schemas": {},
    "securitySchemes": {
      "basicAuth": {
        "type": "http",
        "scheme": "basic"
      },
      "apiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization"
      }
    }
  },
  "security": [
    {
      "basicAuth": []
    }
  ],
  "tags": []
}
```
  • Loading branch information
juliaElastic authored and gergoabraham committed Sep 13, 2024
1 parent 45db1d1 commit c2aec27
Show file tree
Hide file tree
Showing 10 changed files with 849 additions and 95 deletions.
278 changes: 269 additions & 9 deletions x-pack/plugins/fleet/server/routes/package_policy/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { schema } from '@kbn/config-schema';

import { getRouteRequiredAuthz } from '../../services/security';

Expand All @@ -22,9 +23,15 @@ import {
DryRunPackagePoliciesRequestSchema,
DeleteOnePackagePolicyRequestSchema,
BulkGetPackagePoliciesRequestSchema,
PackagePolicyPackageSchema,
PackagePolicyResponseSchema,
PackagePolicyStatusResponseSchema,
DryRunPackagePolicySchema,
} from '../../types';
import { calculateRouteAuthz } from '../../services/security/security';

import { genericErrorResponse, notFoundResponse } from '../schema/errors';

import {
getPackagePoliciesHandler,
getOnePackagePolicyHandler,
Expand All @@ -48,11 +55,31 @@ export const registerRoutes = (router: FleetAuthzRouter) => {
fleetAuthz,
getRouteRequiredAuthz('get', PACKAGE_POLICY_API_ROUTES.LIST_PATTERN)
).granted,
description: 'List package policies',
options: {
tags: ['oas-tag:Fleet package policies'],
},
})
.addVersion(
{
version: API_VERSIONS.public.v1,
validate: { request: GetPackagePoliciesRequestSchema },
validate: {
request: GetPackagePoliciesRequestSchema,
response: {
200: {
body: () =>
schema.object({
items: schema.arrayOf(PackagePolicyResponseSchema),
total: schema.number(),
page: schema.number(),
perPage: schema.number(),
}),
},
400: {
body: genericErrorResponse,
},
},
},
},
getPackagePoliciesHandler
);
Expand All @@ -66,11 +93,31 @@ export const registerRoutes = (router: FleetAuthzRouter) => {
fleetAuthz,
getRouteRequiredAuthz('post', PACKAGE_POLICY_API_ROUTES.BULK_GET_PATTERN)
).granted,
description: 'Bulk get package policies',
options: {
tags: ['oas-tag:Fleet package policies'],
},
})
.addVersion(
{
version: API_VERSIONS.public.v1,
validate: { request: BulkGetPackagePoliciesRequestSchema },
validate: {
request: BulkGetPackagePoliciesRequestSchema,
response: {
200: {
body: () =>
schema.object({
items: schema.arrayOf(PackagePolicyResponseSchema),
}),
},
400: {
body: genericErrorResponse,
},
404: {
body: notFoundResponse,
},
},
},
},
bulkGetPackagePoliciesHandler
);
Expand All @@ -84,11 +131,31 @@ export const registerRoutes = (router: FleetAuthzRouter) => {
fleetAuthz,
getRouteRequiredAuthz('get', PACKAGE_POLICY_API_ROUTES.INFO_PATTERN)
).granted,
description: 'Get package policy by ID',
options: {
tags: ['oas-tag:Fleet package policies'],
},
})
.addVersion(
{
version: API_VERSIONS.public.v1,
validate: { request: GetOnePackagePolicyRequestSchema },
validate: {
request: GetOnePackagePolicyRequestSchema,
response: {
200: {
body: () =>
schema.object({
item: PackagePolicyResponseSchema,
}),
},
400: {
body: genericErrorResponse,
},
404: {
body: notFoundResponse,
},
},
},
},
getOnePackagePolicyHandler
);
Expand All @@ -109,14 +176,35 @@ export const registerRoutes = (router: FleetAuthzRouter) => {
);

// Create
// Authz check moved to service here: https://github.com/elastic/kibana/pull/140458
router.versioned
.post({
path: PACKAGE_POLICY_API_ROUTES.CREATE_PATTERN,
description: 'Create package policy',
options: {
tags: ['oas-tag:Fleet package policies'],
},
})
.addVersion(
{
version: API_VERSIONS.public.v1,
validate: { request: CreatePackagePolicyRequestSchema },
validate: {
request: CreatePackagePolicyRequestSchema,
response: {
200: {
body: () =>
schema.object({
item: PackagePolicyResponseSchema,
}),
},
400: {
body: genericErrorResponse,
},
409: {
body: genericErrorResponse,
},
},
},
},
createPackagePolicyHandler
);
Expand All @@ -130,11 +218,32 @@ export const registerRoutes = (router: FleetAuthzRouter) => {
fleetAuthz,
getRouteRequiredAuthz('put', PACKAGE_POLICY_API_ROUTES.UPDATE_PATTERN)
).granted,
description: 'Update package policy by ID',
options: {
tags: ['oas-tag:Fleet package policies'],
},
})
.addVersion(
{
version: API_VERSIONS.public.v1,
validate: { request: UpdatePackagePolicyRequestSchema },
validate: {
request: UpdatePackagePolicyRequestSchema,
response: {
200: {
body: () =>
schema.object({
item: PackagePolicyResponseSchema,
success: schema.boolean(),
}),
},
400: {
body: genericErrorResponse,
},
403: {
body: genericErrorResponse,
},
},
},
},

updatePackagePolicyHandler
Expand All @@ -147,11 +256,42 @@ export const registerRoutes = (router: FleetAuthzRouter) => {
fleetAuthz: {
integrations: { writeIntegrationPolicies: true },
},
description: 'Bulk delete package policies',
options: {
tags: ['oas-tag:Fleet package policies'],
},
})
.addVersion(
{
version: API_VERSIONS.public.v1,
validate: { request: DeletePackagePoliciesRequestSchema },
validate: {
request: DeletePackagePoliciesRequestSchema,
response: {
200: {
body: () =>
schema.arrayOf(
PackagePolicyStatusResponseSchema.extends({
policy_id: schema.nullable(
schema.maybe(
schema.string({
meta: {
description: 'Use `policy_ids` instead',
deprecated: true,
},
})
)
),
policy_ids: schema.arrayOf(schema.string()),
output_id: schema.nullable(schema.maybe(schema.string())),
package: PackagePolicyPackageSchema,
})
),
},
400: {
body: genericErrorResponse,
},
},
},
},
deletePackagePolicyHandler
);
Expand All @@ -162,11 +302,28 @@ export const registerRoutes = (router: FleetAuthzRouter) => {
fleetAuthz: {
integrations: { writeIntegrationPolicies: true },
},
description: 'Delete package policy by ID',
options: {
tags: ['oas-tag:Fleet package policies'],
},
})
.addVersion(
{
version: API_VERSIONS.public.v1,
validate: { request: DeleteOnePackagePolicyRequestSchema },
validate: {
request: DeleteOnePackagePolicyRequestSchema,
response: {
200: {
body: () =>
schema.object({
id: schema.string(),
}),
},
400: {
body: genericErrorResponse,
},
},
},
},
deleteOnePackagePolicyHandler
);
Expand All @@ -178,11 +335,25 @@ export const registerRoutes = (router: FleetAuthzRouter) => {
fleetAuthz: {
integrations: { writeIntegrationPolicies: true },
},
description: 'Upgrade package policy to a newer package version',
options: {
tags: ['oas-tag:Fleet package policies'],
},
})
.addVersion(
{
version: API_VERSIONS.public.v1,
validate: { request: UpgradePackagePoliciesRequestSchema },
validate: {
request: UpgradePackagePoliciesRequestSchema,
response: {
200: {
body: () => PackagePolicyStatusResponseSchema,
},
400: {
body: genericErrorResponse,
},
},
},
},
upgradePackagePolicyHandler
);
Expand All @@ -194,11 +365,100 @@ export const registerRoutes = (router: FleetAuthzRouter) => {
fleetAuthz: {
integrations: { readIntegrationPolicies: true },
},
description: 'Dry run package policy upgrade',
options: {
tags: ['oas-tag:Fleet package policies'],
},
})
.addVersion(
{
version: API_VERSIONS.public.v1,
validate: { request: DryRunPackagePoliciesRequestSchema },
validate: {
request: DryRunPackagePoliciesRequestSchema,
response: {
200: {
body: () =>
schema.arrayOf(
schema.object({
name: schema.maybe(schema.string()),
statusCode: schema.maybe(schema.number()),
body: schema.maybe(schema.object({ message: schema.string() })),
hasErrors: schema.boolean(),
diff: schema.maybe(
schema.arrayOf(
schema.oneOf([PackagePolicyResponseSchema, DryRunPackagePolicySchema])
)
),
agent_diff: schema.maybe(
schema.arrayOf(
schema.arrayOf(
schema
.object({
id: schema.string(),
name: schema.string(),
revision: schema.number(),
type: schema.string(),
data_stream: schema.object({
namespace: schema.string(),
}),
use_output: schema.string(),
package_policy_id: schema.string(),
meta: schema.maybe(
schema.object({
package: schema
.object({
name: schema.string(),
version: schema.string(),
})
.extendsDeep({
// equivalent of allowing extra keys like `[key: string]: any;`
unknowns: 'allow',
}),
})
),
streams: schema.maybe(
schema.arrayOf(
schema
.object({
id: schema.string(),
data_stream: schema.object({
dataset: schema.string(),
type: schema.string(),
}),
})
.extendsDeep({
unknowns: 'allow',
})
)
),
processors: schema.maybe(
schema.arrayOf(
schema.object({
add_fields: schema.object({
target: schema.string(),
fields: schema.recordOf(
schema.string(),
schema.oneOf([schema.string(), schema.number()])
),
}),
})
)
),
})
.extendsDeep({
unknowns: 'allow',
})
)
)
),
})
),
},
400: {
body: genericErrorResponse,
},
},
},
},
dryRunUpgradePackagePolicyHandler
);
Expand Down
Loading

0 comments on commit c2aec27

Please sign in to comment.