mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Fleet] Add crud UI for fleet proxy (#145017)
This commit is contained in:
parent
59f58e1cbb
commit
bca45a1245
55 changed files with 2259 additions and 131 deletions
|
@ -88,8 +88,9 @@ describe('checking migration metadata changes on all registered SO types', () =>
|
|||
"file": "05c14a75e5e20b12ca514a1d7de231f420facf2c",
|
||||
"file-upload-usage-collection-telemetry": "8478924cf0057bd90df737155b364f98d05420a5",
|
||||
"fileShare": "3f88784b041bb8728a7f40763a08981828799a75",
|
||||
"fleet-fleet-server-host": "f00ca963f1bee868806319789cdc33f1f53a97e2",
|
||||
"fleet-fleet-server-host": "643d15dbf56edb96f7ca65f98409d83ac5792fb6",
|
||||
"fleet-preconfiguration-deletion-record": "7b28f200513c28ae774f1b7d7d7906954e3c6e16",
|
||||
"fleet-proxy": "2bbcd9e6d5e30ac07b275c8d489af07a0d550df5",
|
||||
"graph-workspace": "3342f2cd561afdde8f42f5fb284bf550dee8ebb5",
|
||||
"guided-onboarding-guide-state": "561db8d481b131a2bbf46b1e534d6ce960255135",
|
||||
"guided-onboarding-plugin-state": "a802ed58e9d0076b9632c59d7943861ba476f99c",
|
||||
|
@ -98,7 +99,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
|
|||
"infrastructure-ui-source": "7c8dbbc0a608911f1b683a944f4a65383f6153ed",
|
||||
"ingest-agent-policies": "9170cdad95d887c036b87adf0ff38a3f12800c05",
|
||||
"ingest-download-sources": "1e69dabd6db5e320fe08c5bda8f35f29bafc6b54",
|
||||
"ingest-outputs": "29b867bf7bfd28b1e17c84697dce5c6d078f9705",
|
||||
"ingest-outputs": "4888b16d55a452bf5fff2bb407e0361567eae63a",
|
||||
"ingest-package-policies": "e8707a8c7821ea085e67c2d213e24efa56307393",
|
||||
"ingest_manager_settings": "6f36714825cc15ea8d7cda06fde7851611a532b4",
|
||||
"inventory-view": "bc2bd1e7ec7c186159447ab228d269f22bd39056",
|
||||
|
|
|
@ -59,6 +59,7 @@ const previouslyRegisteredTypes = [
|
|||
'fleet-enrollment-api-keys',
|
||||
'fleet-preconfiguration-deletion-record',
|
||||
'fleet-fleet-server-host',
|
||||
'fleet-proxy',
|
||||
'graph-workspace',
|
||||
'guided-setup-state',
|
||||
'guided-onboarding-guide-state',
|
||||
|
|
|
@ -8,3 +8,5 @@
|
|||
export const FLEET_SERVER_HOST_SAVED_OBJECT_TYPE = 'fleet-fleet-server-host';
|
||||
|
||||
export const DEFAULT_FLEET_SERVER_HOST_ID = 'fleet-default-fleet-server-host';
|
||||
|
||||
export const FLEET_PROXY_SAVED_OBJECT_TYPE = 'fleet-proxy';
|
||||
|
|
|
@ -97,6 +97,14 @@ export const FLEET_SERVER_HOST_API_ROUTES = {
|
|||
DELETE_PATTERN: `${API_ROOT}/fleet_server_hosts/{itemId}`,
|
||||
};
|
||||
|
||||
export const FLEET_PROXY_API_ROUTES = {
|
||||
LIST_PATTERN: `${API_ROOT}/proxies`,
|
||||
CREATE_PATTERN: `${API_ROOT}/proxies`,
|
||||
INFO_PATTERN: `${API_ROOT}/proxies/{itemId}`,
|
||||
UPDATE_PATTERN: `${API_ROOT}/proxies/{itemId}`,
|
||||
DELETE_PATTERN: `${API_ROOT}/proxies/{itemId}`,
|
||||
};
|
||||
|
||||
// Settings API routes
|
||||
export const SETTINGS_API_ROUTES = {
|
||||
INFO_PATTERN: `${API_ROOT}/settings`,
|
||||
|
|
|
@ -4094,6 +4094,242 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/proxies": {
|
||||
"get": {
|
||||
"summary": "Fleet Proxies - List",
|
||||
"description": "Return a list of Proxies",
|
||||
"tags": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/proxies"
|
||||
}
|
||||
},
|
||||
"total": {
|
||||
"type": "integer"
|
||||
},
|
||||
"page": {
|
||||
"type": "integer"
|
||||
},
|
||||
"perPage": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"operationId": "get-fleet-proxies"
|
||||
},
|
||||
"post": {
|
||||
"summary": "Fleet Proxies - Create",
|
||||
"description": "Create a new Fleet Server Host",
|
||||
"tags": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"item": {
|
||||
"$ref": "#/components/schemas/proxies"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"proxy_headers": {
|
||||
"type": "object"
|
||||
},
|
||||
"certificate_authorities": {
|
||||
"type": "string"
|
||||
},
|
||||
"certificate": {
|
||||
"type": "string"
|
||||
},
|
||||
"certificate_key": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"url"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"operationId": "post-fleet-proxies"
|
||||
}
|
||||
},
|
||||
"/proxies/{itemId}": {
|
||||
"get": {
|
||||
"summary": "Fleet Proxies - Info",
|
||||
"tags": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"item": {
|
||||
"$ref": "#/components/schemas/proxies"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"item"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"operationId": "get-one-fleet-proxies"
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": "itemId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"delete": {
|
||||
"summary": "Fleet Proxies - Delete",
|
||||
"operationId": "delete-fleet-proxies",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": "itemId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/parameters/kbn_xsrf"
|
||||
}
|
||||
]
|
||||
},
|
||||
"put": {
|
||||
"summary": "Fleet Proxies - Update",
|
||||
"operationId": "update-fleet-proxies",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"proxy_headers": {
|
||||
"type": "object"
|
||||
},
|
||||
"certificate_authorities": {
|
||||
"type": "string"
|
||||
},
|
||||
"certificate": {
|
||||
"type": "string"
|
||||
},
|
||||
"certificate_key": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"item": {
|
||||
"$ref": "#/components/schemas/proxies"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"item"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": "itemId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/parameters/kbn_xsrf"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
|
@ -5910,6 +6146,37 @@
|
|||
"is_preconfigured",
|
||||
"host_urls"
|
||||
]
|
||||
},
|
||||
"proxies": {
|
||||
"title": "Fleet Proxy",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"proxy_headers": {
|
||||
"type": "object"
|
||||
},
|
||||
"certificate_authorities": {
|
||||
"type": "string"
|
||||
},
|
||||
"certificate": {
|
||||
"type": "string"
|
||||
},
|
||||
"certificate_key": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"url"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -2533,6 +2533,153 @@ paths:
|
|||
in: path
|
||||
required: true
|
||||
- $ref: '#/components/parameters/kbn_xsrf'
|
||||
/proxies:
|
||||
get:
|
||||
summary: Fleet Proxies - List
|
||||
description: Return a list of Proxies
|
||||
tags: []
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/proxies'
|
||||
total:
|
||||
type: integer
|
||||
page:
|
||||
type: integer
|
||||
perPage:
|
||||
type: integer
|
||||
operationId: get-fleet-proxies
|
||||
post:
|
||||
summary: Fleet Proxies - Create
|
||||
description: Create a new Fleet Server Host
|
||||
tags: []
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
item:
|
||||
$ref: '#/components/schemas/proxies'
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
proxy_headers:
|
||||
type: object
|
||||
certificate_authorities:
|
||||
type: string
|
||||
certificate:
|
||||
type: string
|
||||
certificate_key:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- url
|
||||
operationId: post-fleet-proxies
|
||||
/proxies/{itemId}:
|
||||
get:
|
||||
summary: Fleet Proxies - Info
|
||||
tags: []
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
item:
|
||||
$ref: '#/components/schemas/proxies'
|
||||
required:
|
||||
- item
|
||||
operationId: get-one-fleet-proxies
|
||||
parameters:
|
||||
- schema:
|
||||
type: string
|
||||
name: itemId
|
||||
in: path
|
||||
required: true
|
||||
delete:
|
||||
summary: Fleet Proxies - Delete
|
||||
operationId: delete-fleet-proxies
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
parameters:
|
||||
- schema:
|
||||
type: string
|
||||
name: itemId
|
||||
in: path
|
||||
required: true
|
||||
- $ref: '#/components/parameters/kbn_xsrf'
|
||||
put:
|
||||
summary: Fleet Proxies - Update
|
||||
operationId: update-fleet-proxies
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
proxy_headers:
|
||||
type: object
|
||||
certificate_authorities:
|
||||
type: string
|
||||
certificate:
|
||||
type: string
|
||||
certificate_key:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
item:
|
||||
$ref: '#/components/schemas/proxies'
|
||||
required:
|
||||
- item
|
||||
parameters:
|
||||
- schema:
|
||||
type: string
|
||||
name: itemId
|
||||
in: path
|
||||
required: true
|
||||
- $ref: '#/components/parameters/kbn_xsrf'
|
||||
components:
|
||||
securitySchemes:
|
||||
basicAuth:
|
||||
|
@ -3794,5 +3941,26 @@ components:
|
|||
- is_default
|
||||
- is_preconfigured
|
||||
- host_urls
|
||||
proxies:
|
||||
title: Fleet Proxy
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
proxy_headers:
|
||||
type: object
|
||||
certificate_authorities:
|
||||
type: string
|
||||
certificate:
|
||||
type: string
|
||||
certificate_key:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- url
|
||||
security:
|
||||
- basicAuth: []
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
title: Fleet Proxy
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
proxy_headers:
|
||||
type: object
|
||||
certificate_authorities:
|
||||
type: string
|
||||
certificate:
|
||||
type: string
|
||||
certificate_key:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- url
|
|
@ -133,6 +133,11 @@ paths:
|
|||
$ref: paths/fleet_server_hosts.yaml
|
||||
/fleet_server_hosts/{itemId}:
|
||||
$ref: paths/fleet_server_hosts@{item_id}.yaml
|
||||
# Fleet proxies
|
||||
/proxies:
|
||||
$ref: paths/proxies.yaml
|
||||
/proxies/{itemId}:
|
||||
$ref: paths/proxies@{item_id}.yaml
|
||||
components:
|
||||
securitySchemes:
|
||||
basicAuth:
|
||||
|
|
61
x-pack/plugins/fleet/common/openapi/paths/proxies.yaml
Normal file
61
x-pack/plugins/fleet/common/openapi/paths/proxies.yaml
Normal file
|
@ -0,0 +1,61 @@
|
|||
get:
|
||||
summary: Fleet Proxies - List
|
||||
description: Return a list of Proxies
|
||||
tags: []
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
$ref: ../components/schemas/proxies.yaml
|
||||
total:
|
||||
type: integer
|
||||
page:
|
||||
type: integer
|
||||
perPage:
|
||||
type: integer
|
||||
operationId: get-fleet-proxies
|
||||
post:
|
||||
summary: Fleet Proxies - Create
|
||||
description: 'Create a new Fleet Server Host'
|
||||
tags: []
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
item:
|
||||
$ref: ../components/schemas/proxies.yaml
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
proxy_headers:
|
||||
type: object
|
||||
certificate_authorities:
|
||||
type: string
|
||||
certificate:
|
||||
type: string
|
||||
certificate_key:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- url
|
||||
operationId: post-fleet-proxies
|
|
@ -0,0 +1,84 @@
|
|||
get:
|
||||
summary: Fleet Proxies - Info
|
||||
tags: []
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
item:
|
||||
$ref: ../components/schemas/proxies.yaml
|
||||
required:
|
||||
- item
|
||||
operationId: get-one-fleet-proxies
|
||||
parameters:
|
||||
- schema:
|
||||
type: string
|
||||
name: itemId
|
||||
in: path
|
||||
required: true
|
||||
delete:
|
||||
summary: Fleet Proxies - Delete
|
||||
operationId: delete-fleet-proxies
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
parameters:
|
||||
- schema:
|
||||
type: string
|
||||
name: itemId
|
||||
in: path
|
||||
required: true
|
||||
- $ref: ../components/headers/kbn_xsrf.yaml
|
||||
put:
|
||||
summary: Fleet Proxies - Update
|
||||
operationId: update-fleet-proxies
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
proxy_headers:
|
||||
type: object
|
||||
certificate_authorities:
|
||||
type: string
|
||||
certificate:
|
||||
type: string
|
||||
certificate_key:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
item:
|
||||
$ref: ../components/schemas/proxies.yaml
|
||||
required:
|
||||
- item
|
||||
parameters:
|
||||
- schema:
|
||||
type: string
|
||||
name: itemId
|
||||
in: path
|
||||
required: true
|
||||
- $ref: ../components/headers/kbn_xsrf.yaml
|
|
@ -22,6 +22,7 @@ import {
|
|||
PRECONFIGURATION_API_ROUTES,
|
||||
DOWNLOAD_SOURCE_API_ROUTES,
|
||||
FLEET_SERVER_HOST_API_ROUTES,
|
||||
FLEET_PROXY_API_ROUTES,
|
||||
} from '../constants';
|
||||
|
||||
export const epmRouteService = {
|
||||
|
@ -229,6 +230,16 @@ export const outputRoutesService = {
|
|||
getCreateLogstashApiKeyPath: () => OUTPUT_API_ROUTES.LOGSTASH_API_KEY_PATTERN,
|
||||
};
|
||||
|
||||
export const fleetProxiesRoutesService = {
|
||||
getInfoPath: (itemId: string) => FLEET_PROXY_API_ROUTES.INFO_PATTERN.replace('{itemId}', itemId),
|
||||
getUpdatePath: (itemId: string) =>
|
||||
FLEET_PROXY_API_ROUTES.UPDATE_PATTERN.replace('{itemId}', itemId),
|
||||
getListPath: () => FLEET_PROXY_API_ROUTES.LIST_PATTERN,
|
||||
getDeletePath: (itemId: string) =>
|
||||
FLEET_PROXY_API_ROUTES.DELETE_PATTERN.replace('{itemId}', itemId),
|
||||
getCreatePath: () => FLEET_PROXY_API_ROUTES.CREATE_PATTERN,
|
||||
};
|
||||
|
||||
export const fleetServerHostsRoutesService = {
|
||||
getInfoPath: (itemId: string) =>
|
||||
FLEET_SERVER_HOST_API_ROUTES.INFO_PATTERN.replace('{itemId}', itemId),
|
||||
|
|
27
x-pack/plugins/fleet/common/types/models/fleet_proxy.ts
Normal file
27
x-pack/plugins/fleet/common/types/models/fleet_proxy.ts
Normal file
|
@ -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.
|
||||
*/
|
||||
|
||||
interface BaseFleetProxy {
|
||||
name: string;
|
||||
url: string;
|
||||
certificate_authorities?: string | null;
|
||||
certificate?: string | null;
|
||||
certificate_key?: string | null;
|
||||
is_preconfigured: boolean;
|
||||
}
|
||||
|
||||
export interface NewFleetProxy extends BaseFleetProxy {
|
||||
proxy_headers?: Record<string, string | number | boolean> | null;
|
||||
}
|
||||
|
||||
export interface FleetProxy extends NewFleetProxy {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface FleetProxySOAttributes extends BaseFleetProxy {
|
||||
proxy_headers?: string | null;
|
||||
}
|
|
@ -10,6 +10,7 @@ export interface NewFleetServerHost {
|
|||
host_urls: string[];
|
||||
is_default: boolean;
|
||||
is_preconfigured: boolean;
|
||||
proxy_id?: string | null;
|
||||
}
|
||||
|
||||
export interface FleetServerHost extends NewFleetServerHost {
|
||||
|
|
|
@ -17,3 +17,4 @@ export * from './settings';
|
|||
export * from './preconfiguration';
|
||||
export * from './download_sources';
|
||||
export * from './fleet_server_policy_config';
|
||||
export * from './fleet_proxy';
|
||||
|
|
|
@ -25,6 +25,7 @@ export interface NewOutput {
|
|||
certificate?: string;
|
||||
key?: string;
|
||||
} | null;
|
||||
proxy_id?: string | null;
|
||||
}
|
||||
|
||||
export type OutputSOAttributes = NewOutput & {
|
||||
|
|
34
x-pack/plugins/fleet/common/types/rest_spec/fleet_proxies.ts
Normal file
34
x-pack/plugins/fleet/common/types/rest_spec/fleet_proxies.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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 { FleetProxy } from '../models';
|
||||
|
||||
import type { ListResult } from './common';
|
||||
|
||||
export type GetFleetProxiesResponse = ListResult<FleetProxy>;
|
||||
|
||||
export interface PostFleetProxiesRequest {
|
||||
body: {
|
||||
name: string;
|
||||
url: string;
|
||||
proxy_headers?: { [k: string]: string | boolean | number };
|
||||
certificate_autorithies?: string;
|
||||
certificate?: string;
|
||||
certificate_key?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface PutFleetProxiesRequest {
|
||||
body: {
|
||||
name?: string;
|
||||
url?: string;
|
||||
proxy_headers?: { [k: string]: string | boolean | number };
|
||||
certificate_autorithies?: string;
|
||||
certificate?: string;
|
||||
certificate_key?: string;
|
||||
};
|
||||
}
|
|
@ -19,6 +19,7 @@ export interface PutFleetServerHostsRequest {
|
|||
name?: string;
|
||||
host_urls?: string[];
|
||||
is_default?: boolean;
|
||||
proxy_id?: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -28,6 +29,7 @@ export interface PostFleetServerHostsRequest {
|
|||
name?: string;
|
||||
host_urls?: string[];
|
||||
is_default?: boolean;
|
||||
proxy_id?: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -60,6 +60,7 @@ export interface PostOutputRequest {
|
|||
certificate?: string;
|
||||
key?: string;
|
||||
};
|
||||
proxy_id?: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
EuiFlyout,
|
||||
EuiFlyoutBody,
|
||||
EuiFlyoutHeader,
|
||||
EuiTitle,
|
||||
EuiFlyoutFooter,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiButtonEmpty,
|
||||
EuiButton,
|
||||
EuiForm,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { FLYOUT_MAX_WIDTH } from '../../constants';
|
||||
import type { FleetProxy } from '../../../../types';
|
||||
import { TextInput, TextAreaInput } from '../form';
|
||||
|
||||
import { useFleetProxyForm } from './user_fleet_proxy_form';
|
||||
|
||||
export interface FleetProxyFlyoutProps {
|
||||
onClose: () => void;
|
||||
fleetProxy?: FleetProxy;
|
||||
}
|
||||
|
||||
export const FleetProxyFlyout: React.FunctionComponent<FleetProxyFlyoutProps> = ({
|
||||
onClose,
|
||||
fleetProxy,
|
||||
}) => {
|
||||
// const { docLinks } = useStartServices();
|
||||
|
||||
const form = useFleetProxyForm(fleetProxy, onClose);
|
||||
const { inputs } = form;
|
||||
|
||||
return (
|
||||
<EuiFlyout maxWidth={FLYOUT_MAX_WIDTH} onClose={onClose}>
|
||||
<EuiFlyoutHeader hasBorder={true}>
|
||||
<EuiTitle size="m">
|
||||
<h2>
|
||||
{fleetProxy ? (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.fleetProxyFlyout.editTitle"
|
||||
defaultMessage="Edit Proxy"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.fleetProxyFlyout.addTitle"
|
||||
defaultMessage="Add Proxy"
|
||||
/>
|
||||
)}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<EuiForm onSubmit={form.submit}>
|
||||
<TextInput
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.fleetProxyFlyout.nameInputLabel"
|
||||
defaultMessage="Name"
|
||||
/>
|
||||
}
|
||||
inputProps={inputs.nameInput}
|
||||
data-test-subj="fleetProxyFlyout.nameInput"
|
||||
placeholder={i18n.translate(
|
||||
'xpack.fleet.settings.fleetProxyFlyout.nameInputPlaceholder',
|
||||
{
|
||||
defaultMessage: 'Specify name',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
<TextInput
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.fleetProxyFlyout.urlInputLabel"
|
||||
defaultMessage="Proxy Url"
|
||||
/>
|
||||
}
|
||||
dataTestSubj="fleetProxyFlyout.urlInput"
|
||||
inputProps={inputs.urlInput}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.fleet.settings.fleetProxyFlyout.urlInputPlaceholder',
|
||||
{ defaultMessage: 'Specify proxy url' }
|
||||
)}
|
||||
/>
|
||||
<TextAreaInput
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.fleetProxyFlyout.proxyHeadersLabel"
|
||||
defaultMessage="Proxy headers"
|
||||
/>
|
||||
}
|
||||
dataTestSubj="fleetProxyFlyout.proxyHeadersInput"
|
||||
inputProps={inputs.proxyHeadersInput}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.fleet.settings.fleetProxyFlyout.proxyHeadersPlaceholder',
|
||||
{ defaultMessage: 'Specify proxy headers' }
|
||||
)}
|
||||
/>
|
||||
<TextInput
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.fleetProxyFlyout.certificateAuthoritiesLabel"
|
||||
defaultMessage="Certificate authorities"
|
||||
/>
|
||||
}
|
||||
dataTestSubj="fleetProxyFlyout.certificateAuthoritiesInput"
|
||||
inputProps={inputs.certificateAuthoritiesInput}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.fleet.settings.fleetProxyFlyout.certificateAuthoritiesPlaceholder',
|
||||
{ defaultMessage: 'Specify certificate authorities' }
|
||||
)}
|
||||
/>
|
||||
<TextInput
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.fleetProxyFlyout.certificateLabel"
|
||||
defaultMessage="Certificate"
|
||||
/>
|
||||
}
|
||||
dataTestSubj="fleetProxyFlyout.certificateInput"
|
||||
inputProps={inputs.certificateInput}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.fleet.settings.fleetProxyFlyout.certificatePlaceholder',
|
||||
{ defaultMessage: 'Specify certificate' }
|
||||
)}
|
||||
/>
|
||||
<TextInput
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.fleetProxyFlyout.certificateKeyLabel"
|
||||
defaultMessage="Certificate key"
|
||||
/>
|
||||
}
|
||||
dataTestSubj="fleetProxyFlyout.certificateKeyInput"
|
||||
inputProps={inputs.certificateKeyInput}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.fleet.settings.fleetProxyFlyout.certificateKeyPlaceholder',
|
||||
{ defaultMessage: 'Specify certificate key' }
|
||||
)}
|
||||
/>
|
||||
</EuiForm>
|
||||
</EuiFlyoutBody>
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty onClick={() => onClose()} flush="left">
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.fleetProxyFlyout.cancelButtonLabel"
|
||||
defaultMessage="Cancel"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
fill
|
||||
isLoading={form.isLoading}
|
||||
isDisabled={form.isDisabled}
|
||||
onClick={form.submit}
|
||||
data-test-subj="saveApplySettingsBtn"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.fleetProxyFlyout.saveButton"
|
||||
defaultMessage="Save and apply settings"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
</EuiFlyout>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* 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 React, { useCallback, useState, useMemo } from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { safeDump, safeLoad } from 'js-yaml';
|
||||
|
||||
import {
|
||||
sendPostFleetProxy,
|
||||
sendPutFleetProxy,
|
||||
useInput,
|
||||
useStartServices,
|
||||
validateInputs,
|
||||
} from '../../../../hooks';
|
||||
|
||||
import { useConfirmModal } from '../../hooks/use_confirm_modal';
|
||||
import type { FleetProxy } from '../../../../types';
|
||||
|
||||
const URL_REGEX = /^(http)(s)?:\/\/[^\s$.?#].[^\s]*$/gm;
|
||||
|
||||
const ConfirmTitle = () => (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.fleetProxyFlyout.confirmModalTitle"
|
||||
defaultMessage="Save and deploy changes?"
|
||||
/>
|
||||
);
|
||||
|
||||
const ConfirmDescription: React.FunctionComponent = ({}) => (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.fleetProxyFlyout.confirmModalText"
|
||||
defaultMessage="This action will update agent policies using that proxies. This action can not be undone. Are you sure you wish to continue?"
|
||||
/>
|
||||
);
|
||||
|
||||
function validateUrl(value: string) {
|
||||
if (!value || value === '') {
|
||||
return [
|
||||
i18n.translate('xpack.fleet.settings.fleetProxyFlyoutUrlRequired', {
|
||||
defaultMessage: 'URL is required',
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
if (!value.match(URL_REGEX)) {
|
||||
return [
|
||||
i18n.translate('xpack.fleet.settings.fleetProxyFlyoutUrlError', {
|
||||
defaultMessage: 'Invalid URL',
|
||||
}),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
function validateProxyHeaders(value: string) {
|
||||
if (value && value !== '') {
|
||||
const res = safeLoad(value);
|
||||
if (
|
||||
typeof res !== 'object' ||
|
||||
Object.values(res).some((val) => {
|
||||
const valType = typeof val;
|
||||
return valType !== 'string' && valType !== 'number' && valType !== 'boolean';
|
||||
})
|
||||
) {
|
||||
return [
|
||||
i18n.translate('xpack.fleet.settings.fleetProxy.proxyHeadersErrorMessage', {
|
||||
defaultMessage: 'Proxy headers is not a valid key: value object.',
|
||||
}),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function validateName(value: string) {
|
||||
if (!value || value === '') {
|
||||
return [
|
||||
i18n.translate('xpack.fleet.settings.fleetProxy.nameIsRequiredErrorMessage', {
|
||||
defaultMessage: 'Name is required',
|
||||
}),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export function useFleetProxyForm(fleetProxy: FleetProxy | undefined, onSuccess: () => void) {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { notifications } = useStartServices();
|
||||
const { confirm } = useConfirmModal();
|
||||
const isPreconfigured = fleetProxy?.is_preconfigured ?? false;
|
||||
|
||||
const nameInput = useInput(fleetProxy?.name ?? '', validateName, isPreconfigured);
|
||||
const urlInput = useInput(fleetProxy?.url ?? '', validateUrl, isPreconfigured);
|
||||
const proxyHeadersInput = useInput(
|
||||
fleetProxy?.proxy_headers ? safeDump(fleetProxy.proxy_headers) : '',
|
||||
validateProxyHeaders,
|
||||
isPreconfigured
|
||||
);
|
||||
const certificateAuthoritiesInput = useInput(
|
||||
fleetProxy?.certificate_authorities ?? '',
|
||||
() => undefined,
|
||||
isPreconfigured
|
||||
);
|
||||
const certificateInput = useInput(
|
||||
fleetProxy?.certificate ?? '',
|
||||
() => undefined,
|
||||
isPreconfigured
|
||||
);
|
||||
const certificateKeyInput = useInput(
|
||||
fleetProxy?.certificate_key ?? '',
|
||||
() => undefined,
|
||||
isPreconfigured
|
||||
);
|
||||
|
||||
const inputs = useMemo(
|
||||
() => ({
|
||||
nameInput,
|
||||
urlInput,
|
||||
proxyHeadersInput,
|
||||
certificateAuthoritiesInput,
|
||||
certificateInput,
|
||||
certificateKeyInput,
|
||||
}),
|
||||
[
|
||||
nameInput,
|
||||
urlInput,
|
||||
proxyHeadersInput,
|
||||
certificateAuthoritiesInput,
|
||||
certificateInput,
|
||||
certificateKeyInput,
|
||||
]
|
||||
);
|
||||
|
||||
const validate = useCallback(() => validateInputs(inputs), [inputs]);
|
||||
|
||||
const submit = useCallback(async () => {
|
||||
try {
|
||||
if (!validate()) {
|
||||
return;
|
||||
}
|
||||
if (fleetProxy && !(await confirm(<ConfirmTitle />, <ConfirmDescription />))) {
|
||||
return;
|
||||
}
|
||||
setIsLoading(true);
|
||||
const data = {
|
||||
name: nameInput.value,
|
||||
url: urlInput.value,
|
||||
proxy_headers:
|
||||
proxyHeadersInput.value === '' ? undefined : safeLoad(proxyHeadersInput.value),
|
||||
certificate_authorities: certificateAuthoritiesInput.value,
|
||||
certificate: certificateInput.value,
|
||||
certificate_key: certificateKeyInput.value,
|
||||
};
|
||||
if (fleetProxy) {
|
||||
const res = await sendPutFleetProxy(fleetProxy.id, data);
|
||||
if (res.error) {
|
||||
throw res.error;
|
||||
}
|
||||
} else {
|
||||
const res = await sendPostFleetProxy(data);
|
||||
if (res.error) {
|
||||
throw res.error;
|
||||
}
|
||||
}
|
||||
notifications.toasts.addSuccess(
|
||||
i18n.translate('xpack.fleet.settings.fleetProxyFlyout.successToastTitle', {
|
||||
defaultMessage: 'Fleet proxy saved',
|
||||
})
|
||||
);
|
||||
setIsLoading(false);
|
||||
await onSuccess();
|
||||
} catch (error) {
|
||||
setIsLoading(false);
|
||||
notifications.toasts.addError(error, {
|
||||
title: i18n.translate('xpack.fleet.settings.fleetProxyFlyout.errorToastTitle', {
|
||||
defaultMessage: 'An error happened while saving Fleet Server host',
|
||||
}),
|
||||
});
|
||||
}
|
||||
}, [
|
||||
fleetProxy,
|
||||
nameInput.value,
|
||||
urlInput.value,
|
||||
proxyHeadersInput.value,
|
||||
certificateAuthoritiesInput.value,
|
||||
certificateInput.value,
|
||||
certificateKeyInput.value,
|
||||
validate,
|
||||
notifications,
|
||||
confirm,
|
||||
onSuccess,
|
||||
]);
|
||||
|
||||
const hasChanged = Object.values(inputs).some((input) => input.hasChanged);
|
||||
|
||||
const isDisabled =
|
||||
isLoading || !hasChanged || nameInput.props.isInvalid || urlInput.props.isInvalid;
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
isDisabled,
|
||||
submit,
|
||||
inputs,
|
||||
};
|
||||
}
|
|
@ -29,7 +29,9 @@ const mockedUsedFleetStatus = useFleetStatus as jest.MockedFunction<typeof useFl
|
|||
function renderFlyout(output?: Output) {
|
||||
const renderer = createFleetTestRendererMock();
|
||||
|
||||
const utils = renderer.render(<EditOutputFlyout output={output} onClose={() => {}} />);
|
||||
const utils = renderer.render(
|
||||
<EditOutputFlyout proxies={[]} output={output} onClose={() => {}} />
|
||||
);
|
||||
|
||||
return { utils };
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import {
|
||||
EuiFlyout,
|
||||
|
@ -26,11 +26,12 @@ import {
|
|||
EuiCallOut,
|
||||
EuiSpacer,
|
||||
EuiLink,
|
||||
EuiComboBox,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { MultiRowInput } from '../multi_row_input';
|
||||
import type { Output } from '../../../../types';
|
||||
import type { Output, FleetProxy } from '../../../../types';
|
||||
import { FLYOUT_MAX_WIDTH } from '../../constants';
|
||||
import { LogstashInstructions } from '../logstash_instructions';
|
||||
import { useBreadcrumbs, useStartServices } from '../../../../hooks';
|
||||
|
@ -42,6 +43,7 @@ import { EncryptionKeyRequiredCallout } from './encryption_key_required_callout'
|
|||
export interface EditOutputFlyoutProps {
|
||||
output?: Output;
|
||||
onClose: () => void;
|
||||
proxies: FleetProxy[];
|
||||
}
|
||||
|
||||
const OUTPUT_TYPE_OPTIONS = [
|
||||
|
@ -52,12 +54,18 @@ const OUTPUT_TYPE_OPTIONS = [
|
|||
export const EditOutputFlyout: React.FunctionComponent<EditOutputFlyoutProps> = ({
|
||||
onClose,
|
||||
output,
|
||||
proxies,
|
||||
}) => {
|
||||
useBreadcrumbs('settings');
|
||||
const form = useOutputForm(onClose, output);
|
||||
const inputs = form.inputs;
|
||||
const { docLinks } = useStartServices();
|
||||
|
||||
const proxiesOptions = useMemo(
|
||||
() => proxies.map((proxy) => ({ value: proxy.id, label: proxy.name })),
|
||||
[proxies]
|
||||
);
|
||||
|
||||
const isLogstashOutput = inputs.typeInput.value === 'logstash';
|
||||
const isESOutput = inputs.typeInput.value === 'elasticsearch';
|
||||
|
||||
|
@ -301,6 +309,36 @@ export const EditOutputFlyout: React.FunctionComponent<EditOutputFlyoutProps> =
|
|||
/>
|
||||
</EuiFormRow>
|
||||
)}
|
||||
<EuiFormRow
|
||||
fullWidth
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.editOutputFlyout.proxyIdLabel"
|
||||
defaultMessage="Proxy"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiComboBox
|
||||
fullWidth
|
||||
data-test-subj="settingsOutputsFlyout.proxyIdInput"
|
||||
{...inputs.proxyIdInput.props}
|
||||
onChange={(options) => inputs.proxyIdInput.setValue(options?.[0]?.value ?? '')}
|
||||
selectedOptions={
|
||||
inputs.proxyIdInput.value !== ''
|
||||
? proxiesOptions.filter((option) => option.value === inputs.proxyIdInput.value)
|
||||
: []
|
||||
}
|
||||
options={proxiesOptions}
|
||||
singleSelection={{ asPlainText: true }}
|
||||
isClearable={true}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.fleet.settings.editOutputFlyout.proxyIdPlaceholder',
|
||||
{
|
||||
defaultMessage: 'Select proxy',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.fleet.settings.editOutputFlyout.yamlConfigInputLabel', {
|
||||
defaultMessage: 'Advanced YAML configuration',
|
||||
|
|
|
@ -96,6 +96,8 @@ export function useOutputForm(onSucess: () => void, output?: Output) {
|
|||
isPreconfigured
|
||||
);
|
||||
|
||||
const proxyIdInput = useInput(output?.proxy_id ?? '', () => undefined, isPreconfigured);
|
||||
|
||||
const sslKeyInput = useInput(output?.ssl?.key ?? '', validateSSLKey, isPreconfigured);
|
||||
|
||||
const isLogstash = typeInput.value === 'logstash';
|
||||
|
@ -112,6 +114,7 @@ export function useOutputForm(onSucess: () => void, output?: Output) {
|
|||
sslCertificateInput,
|
||||
sslKeyInput,
|
||||
sslCertificateAuthoritiesInput,
|
||||
proxyIdInput,
|
||||
};
|
||||
|
||||
const hasChanged = Object.values(inputs).some((input) => input.hasChanged);
|
||||
|
@ -161,6 +164,7 @@ export function useOutputForm(onSucess: () => void, output?: Output) {
|
|||
}
|
||||
setIsloading(true);
|
||||
|
||||
const proxyIdValue = proxyIdInput.value !== '' ? proxyIdInput.value : null;
|
||||
const data: PostOutputRequest['body'] = isLogstash
|
||||
? {
|
||||
name: nameInput.value,
|
||||
|
@ -176,6 +180,7 @@ export function useOutputForm(onSucess: () => void, output?: Output) {
|
|||
(val) => val !== ''
|
||||
),
|
||||
},
|
||||
proxy_id: proxyIdValue,
|
||||
}
|
||||
: {
|
||||
name: nameInput.value,
|
||||
|
@ -185,6 +190,7 @@ export function useOutputForm(onSucess: () => void, output?: Output) {
|
|||
is_default_monitoring: defaultMonitoringOutputInput.value,
|
||||
config_yaml: additionalYamlConfigInput.value,
|
||||
ca_trusted_fingerprint: caTrustedFingerprintInput.value,
|
||||
proxy_id: proxyIdValue,
|
||||
};
|
||||
|
||||
if (output) {
|
||||
|
@ -231,6 +237,7 @@ export function useOutputForm(onSucess: () => void, output?: Output) {
|
|||
sslKeyInput.value,
|
||||
nameInput.value,
|
||||
typeInput.value,
|
||||
proxyIdInput.value,
|
||||
|
||||
notifications.toasts,
|
||||
onSucess,
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* 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 React, { useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { EuiBasicTable, EuiButtonIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import type { EuiBasicTableColumn } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { useLink } from '../../../../hooks';
|
||||
import type { FleetProxy } from '../../../../types';
|
||||
|
||||
export interface FleetProxiesTableProps {
|
||||
proxies: FleetProxy[];
|
||||
deleteFleetProxy: (ds: FleetProxy) => void;
|
||||
}
|
||||
|
||||
const NameFlexItemWithMaxWidth = styled(EuiFlexItem)`
|
||||
max-width: 250px;
|
||||
`;
|
||||
|
||||
export const FleetProxiesTable: React.FunctionComponent<FleetProxiesTableProps> = ({
|
||||
proxies,
|
||||
deleteFleetProxy,
|
||||
}) => {
|
||||
const { getHref } = useLink();
|
||||
|
||||
const columns = useMemo((): Array<EuiBasicTableColumn<FleetProxy>> => {
|
||||
return [
|
||||
{
|
||||
render: (fleetProxy: FleetProxy) => (
|
||||
<EuiFlexGroup alignItems="center" gutterSize="xs">
|
||||
<NameFlexItemWithMaxWidth grow={false}>
|
||||
<p
|
||||
title={fleetProxy.name}
|
||||
className={`eui-textTruncate`}
|
||||
data-test-subj="fleetProxiesTable.name"
|
||||
>
|
||||
{fleetProxy.name}
|
||||
</p>
|
||||
</NameFlexItemWithMaxWidth>
|
||||
</EuiFlexGroup>
|
||||
),
|
||||
width: '288px',
|
||||
name: i18n.translate('xpack.fleet.settings.fleetProxiesTable.nameColumnTitle', {
|
||||
defaultMessage: 'Name',
|
||||
}),
|
||||
},
|
||||
{
|
||||
truncateText: true,
|
||||
field: 'url',
|
||||
name: i18n.translate('xpack.fleet.settings.fleetProxiesTable.urlColumnTitle', {
|
||||
defaultMessage: 'Url',
|
||||
}),
|
||||
},
|
||||
{
|
||||
width: '68px',
|
||||
render: (fleetProxy: FleetProxy) => {
|
||||
const isDeleteVisible = true;
|
||||
|
||||
return (
|
||||
<EuiFlexGroup gutterSize="s" justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={false}>
|
||||
{isDeleteVisible && (
|
||||
<EuiButtonIcon
|
||||
color="text"
|
||||
iconType="trash"
|
||||
onClick={() => deleteFleetProxy(fleetProxy)}
|
||||
title={i18n.translate(
|
||||
'xpack.fleet.settings.fleetProxiesTable.deleteButtonTitle',
|
||||
{
|
||||
defaultMessage: 'Delete',
|
||||
}
|
||||
)}
|
||||
data-test-subj="fleetProxiesTable.delete.btn"
|
||||
/>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonIcon
|
||||
color="text"
|
||||
iconType="pencil"
|
||||
href={getHref('settings_edit_fleet_proxy', {
|
||||
itemId: fleetProxy.id,
|
||||
})}
|
||||
title={i18n.translate('xpack.fleet.settings.fleetProxiesTable.editButtonTitle', {
|
||||
defaultMessage: 'Edit',
|
||||
})}
|
||||
data-test-subj="fleetProxiesTable.edit.btn"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
},
|
||||
name: i18n.translate('xpack.fleet.settings.fleetProxiesTable.actionsColumnTitle', {
|
||||
defaultMessage: 'Actions',
|
||||
}),
|
||||
},
|
||||
];
|
||||
}, [deleteFleetProxy, getHref]);
|
||||
|
||||
return <EuiBasicTable columns={columns} items={proxies} data-test-subj="fleetProxiesTable" />;
|
||||
};
|
|
@ -32,7 +32,7 @@ const fleetServerHost = {
|
|||
export const FleetServerHostsFlyout = ({ width }: Args) => {
|
||||
return (
|
||||
<div style={{ width }}>
|
||||
<Component onClose={() => {}} fleetServerHost={fleetServerHost} />
|
||||
<Component proxies={[]} onClose={() => {}} fleetServerHost={fleetServerHost} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
|
@ -23,31 +23,39 @@ import {
|
|||
EuiSpacer,
|
||||
EuiForm,
|
||||
EuiFormRow,
|
||||
EuiFieldText,
|
||||
EuiSwitch,
|
||||
EuiComboBox,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { MultiRowInput } from '../multi_row_input';
|
||||
import { useStartServices } from '../../../../hooks';
|
||||
import { FLYOUT_MAX_WIDTH } from '../../constants';
|
||||
import type { FleetServerHost } from '../../../../types';
|
||||
import type { FleetServerHost, FleetProxy } from '../../../../types';
|
||||
import { TextInput } from '../form';
|
||||
|
||||
import { useFleetServerHostsForm } from './use_fleet_server_host_form';
|
||||
|
||||
export interface FleetServerHostsFlyoutProps {
|
||||
onClose: () => void;
|
||||
fleetServerHost?: FleetServerHost;
|
||||
proxies: FleetProxy[];
|
||||
}
|
||||
|
||||
export const FleetServerHostsFlyout: React.FunctionComponent<FleetServerHostsFlyoutProps> = ({
|
||||
onClose,
|
||||
fleetServerHost,
|
||||
proxies,
|
||||
}) => {
|
||||
const { docLinks } = useStartServices();
|
||||
|
||||
const form = useFleetServerHostsForm(fleetServerHost, onClose);
|
||||
const { inputs } = form;
|
||||
|
||||
const proxiesOptions = useMemo(
|
||||
() => proxies.map((proxy) => ({ value: proxy.id, label: proxy.name })),
|
||||
[proxies]
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiFlyout maxWidth={FLYOUT_MAX_WIDTH} onClose={onClose}>
|
||||
<EuiFlyoutHeader hasBorder={true}>
|
||||
|
@ -69,28 +77,20 @@ export const FleetServerHostsFlyout: React.FunctionComponent<FleetServerHostsFly
|
|||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<EuiForm onSubmit={form.submit}>
|
||||
<EuiFormRow
|
||||
fullWidth
|
||||
<TextInput
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.fleetServerHostsFlyout.nameInputLabel"
|
||||
defaultMessage="Name"
|
||||
/>
|
||||
}
|
||||
{...inputs.nameInput.formRowProps}
|
||||
>
|
||||
<EuiFieldText
|
||||
data-test-subj="fleetServerHostsFlyout.nameInput"
|
||||
fullWidth
|
||||
{...inputs.nameInput.props}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.fleet.settings.fleetServerHostsFlyout.nameInputPlaceholder',
|
||||
{
|
||||
defaultMessage: 'Specify name',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
inputProps={inputs.nameInput}
|
||||
dataTestSubj="fleetServerHostsFlyout.nameInput"
|
||||
placeholder={i18n.translate(
|
||||
'xpack.fleet.settings.fleetServerHostsFlyout.nameInputPlaceholder',
|
||||
{ defaultMessage: 'Specify name' }
|
||||
)}
|
||||
/>
|
||||
<EuiFormRow
|
||||
fullWidth
|
||||
label={
|
||||
|
@ -134,6 +134,36 @@ export const FleetServerHostsFlyout: React.FunctionComponent<FleetServerHostsFly
|
|||
/>
|
||||
</>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
fullWidth
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.fleetServerHostsFlyout.proxyIdLabel"
|
||||
defaultMessage="Proxy"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiComboBox
|
||||
fullWidth
|
||||
data-test-subj="fleetServerHostsFlyout.proxyIdInput"
|
||||
{...inputs.proxyIdInput.props}
|
||||
onChange={(options) => inputs.proxyIdInput.setValue(options?.[0]?.value ?? '')}
|
||||
selectedOptions={
|
||||
inputs.proxyIdInput.value !== ''
|
||||
? proxiesOptions.filter((option) => option.value === inputs.proxyIdInput.value)
|
||||
: []
|
||||
}
|
||||
options={proxiesOptions}
|
||||
singleSelection={{ asPlainText: true }}
|
||||
isClearable={true}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.fleet.settings.fleetServerHostsFlyout.proxyIdPlaceholder',
|
||||
{
|
||||
defaultMessage: 'Select proxy',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow fullWidth {...inputs.isDefaultInput.formRowProps}>
|
||||
<EuiSwitch
|
||||
data-test-subj="fleetServerHostsFlyout.isDefaultSwitch"
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
// copy this one
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
@ -17,6 +17,7 @@ import {
|
|||
useInput,
|
||||
useStartServices,
|
||||
useSwitchInput,
|
||||
validateInputs,
|
||||
} from '../../../../hooks';
|
||||
import { isDiffPathProtocol } from '../../../../../../../common/services';
|
||||
import { useConfirmModal } from '../../hooks/use_confirm_modal';
|
||||
|
@ -133,12 +134,20 @@ export function useFleetServerHostsForm(
|
|||
validateFleetServerHosts,
|
||||
isPreconfigured
|
||||
);
|
||||
const proxyIdInput = useInput(fleetServerHost?.proxy_id ?? '', () => undefined, isPreconfigured);
|
||||
|
||||
const validate = useCallback(
|
||||
() => hostUrlsInput.validate() && nameInput.validate(),
|
||||
[hostUrlsInput, nameInput]
|
||||
const inputs = useMemo(
|
||||
() => ({
|
||||
nameInput,
|
||||
isDefaultInput,
|
||||
hostUrlsInput,
|
||||
proxyIdInput,
|
||||
}),
|
||||
[nameInput, isDefaultInput, hostUrlsInput, proxyIdInput]
|
||||
);
|
||||
|
||||
const validate = useCallback(() => validateInputs(inputs), [inputs]);
|
||||
|
||||
const submit = useCallback(async () => {
|
||||
try {
|
||||
if (!validate()) {
|
||||
|
@ -148,21 +157,19 @@ export function useFleetServerHostsForm(
|
|||
return;
|
||||
}
|
||||
setIsLoading(true);
|
||||
const data = {
|
||||
name: nameInput.value,
|
||||
host_urls: hostUrlsInput.value,
|
||||
is_default: isDefaultInput.value,
|
||||
proxy_id: proxyIdInput.value !== '' ? proxyIdInput.value : null,
|
||||
};
|
||||
if (fleetServerHost) {
|
||||
const res = await sendPutFleetServerHost(fleetServerHost.id, {
|
||||
name: nameInput.value,
|
||||
host_urls: hostUrlsInput.value,
|
||||
is_default: isDefaultInput.value,
|
||||
});
|
||||
const res = await sendPutFleetServerHost(fleetServerHost.id, data);
|
||||
if (res.error) {
|
||||
throw res.error;
|
||||
}
|
||||
} else {
|
||||
const res = await sendPostFleetServerHost({
|
||||
name: nameInput.value,
|
||||
host_urls: hostUrlsInput.value,
|
||||
is_default: isDefaultInput.value,
|
||||
});
|
||||
const res = await sendPostFleetServerHost(data);
|
||||
if (res.error) {
|
||||
throw res.error;
|
||||
}
|
||||
|
@ -187,6 +194,7 @@ export function useFleetServerHostsForm(
|
|||
nameInput.value,
|
||||
hostUrlsInput.value,
|
||||
isDefaultInput.value,
|
||||
proxyIdInput.value,
|
||||
validate,
|
||||
notifications,
|
||||
confirm,
|
||||
|
@ -195,7 +203,10 @@ export function useFleetServerHostsForm(
|
|||
|
||||
const isDisabled =
|
||||
isLoading ||
|
||||
(!hostUrlsInput.hasChanged && !isDefaultInput.hasChanged && !nameInput.hasChanged) ||
|
||||
(!hostUrlsInput.hasChanged &&
|
||||
!isDefaultInput.hasChanged &&
|
||||
!nameInput.hasChanged &&
|
||||
!proxyIdInput.hasChanged) ||
|
||||
hostUrlsInput.props.isInvalid ||
|
||||
nameInput.props.isInvalid;
|
||||
|
||||
|
@ -203,10 +214,6 @@ export function useFleetServerHostsForm(
|
|||
isLoading,
|
||||
isDisabled,
|
||||
submit,
|
||||
inputs: {
|
||||
hostUrlsInput,
|
||||
nameInput,
|
||||
isDefaultInput,
|
||||
},
|
||||
inputs,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { EuiFieldText, EuiFormRow, EuiTextArea } from '@elastic/eui';
|
||||
|
||||
interface InputProps {
|
||||
label: ReactNode;
|
||||
placeholder?: string;
|
||||
dataTestSubj?: string;
|
||||
inputProps: {
|
||||
props: {
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
|
||||
value: string;
|
||||
isInvalid?: boolean;
|
||||
disabled?: boolean;
|
||||
};
|
||||
formRowProps: {
|
||||
error?: string[];
|
||||
isInvalid?: boolean;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const TextInput: React.FunctionComponent<InputProps> = ({
|
||||
label,
|
||||
inputProps,
|
||||
placeholder,
|
||||
dataTestSubj,
|
||||
}) => (
|
||||
<EuiFormRow fullWidth label={label} {...inputProps.formRowProps}>
|
||||
<EuiFieldText
|
||||
data-test-subj={dataTestSubj}
|
||||
fullWidth
|
||||
{...inputProps.props}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
);
|
||||
|
||||
export const TextAreaInput: React.FunctionComponent<InputProps> = ({
|
||||
label,
|
||||
inputProps,
|
||||
placeholder,
|
||||
dataTestSubj,
|
||||
}) => (
|
||||
<EuiFormRow fullWidth label={label} {...inputProps.formRowProps}>
|
||||
<EuiTextArea
|
||||
fullWidth
|
||||
rows={5}
|
||||
data-test-subj={dataTestSubj}
|
||||
{...inputProps.props}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
);
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { EuiTitle, EuiText, EuiSpacer, EuiButtonEmpty } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { useLink } from '../../../../hooks';
|
||||
import type { FleetProxy } from '../../../../types';
|
||||
import { FleetProxiesTable } from '../fleet_proxies_table';
|
||||
|
||||
export interface FleetProxiesSectionProps {
|
||||
proxies: FleetProxy[];
|
||||
deleteFleetProxy: (proxy: FleetProxy) => void;
|
||||
}
|
||||
|
||||
export const FleetProxiesSection: React.FunctionComponent<FleetProxiesSectionProps> = ({
|
||||
proxies,
|
||||
deleteFleetProxy,
|
||||
}) => {
|
||||
const { getHref } = useLink();
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiTitle size="s">
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.fleetProxiesSection.title"
|
||||
defaultMessage="Proxies"
|
||||
/>
|
||||
</h4>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="xs" />
|
||||
<EuiText color="subdued" size="m">
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.fleetProxiesSection.subtitle"
|
||||
defaultMessage="Specify any proxy URLs to be used in Fleet servers or Outputs."
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer size="m" />
|
||||
<FleetProxiesTable proxies={proxies} deleteFleetProxy={deleteFleetProxy} />
|
||||
<EuiSpacer size="s" />
|
||||
<EuiButtonEmpty
|
||||
iconType="plusInCircle"
|
||||
href={getHref('settings_create_fleet_proxy')}
|
||||
data-test-subj="addProxyBtn"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.fleetProxiesSection.CreateButtonLabel"
|
||||
defaultMessage="Add proxy"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -8,28 +8,33 @@
|
|||
import React from 'react';
|
||||
import { EuiSpacer } from '@elastic/eui';
|
||||
|
||||
import type { Output, DownloadSource, FleetServerHost } from '../../../../types';
|
||||
import type { Output, DownloadSource, FleetServerHost, FleetProxy } from '../../../../types';
|
||||
|
||||
import { FleetServerHostsSection } from './fleet_server_hosts_section';
|
||||
import { OutputSection } from './output_section';
|
||||
import { AgentBinarySection } from './agent_binary_section';
|
||||
import { FleetProxiesSection } from './fleet_proxies_section';
|
||||
|
||||
export interface SettingsPageProps {
|
||||
outputs: Output[];
|
||||
proxies: FleetProxy[];
|
||||
fleetServerHosts: FleetServerHost[];
|
||||
deleteOutput: (output: Output) => void;
|
||||
deleteFleetServerHost: (fleetServerHost: FleetServerHost) => void;
|
||||
downloadSources: DownloadSource[];
|
||||
deleteDownloadSource: (ds: DownloadSource) => void;
|
||||
deleteFleetProxy: (proxy: FleetProxy) => void;
|
||||
}
|
||||
|
||||
export const SettingsPage: React.FunctionComponent<SettingsPageProps> = ({
|
||||
outputs,
|
||||
proxies,
|
||||
fleetServerHosts,
|
||||
deleteOutput,
|
||||
deleteFleetServerHost,
|
||||
downloadSources,
|
||||
deleteDownloadSource,
|
||||
deleteFleetProxy,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
|
@ -45,6 +50,8 @@ export const SettingsPage: React.FunctionComponent<SettingsPageProps> = ({
|
|||
downloadSources={downloadSources}
|
||||
deleteDownloadSource={deleteDownloadSource}
|
||||
/>
|
||||
<EuiSpacer size="m" />
|
||||
<FleetProxiesSection proxies={proxies} deleteFleetProxy={deleteFleetProxy} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -4,3 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './use_delete_proxy';
|
||||
export * from './use_delete_output';
|
||||
export * from './use_delete_fleet_server_host';
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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 React, { useCallback } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { sendDeleteFleetProxy, useStartServices } from '../../../hooks';
|
||||
import type { FleetProxy } from '../../../types';
|
||||
|
||||
import { useConfirmModal } from './use_confirm_modal';
|
||||
|
||||
const ConfirmTitle = () => (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.deleteFleetProxy.confirmModalTitle"
|
||||
defaultMessage="Delete and deploy changes?"
|
||||
/>
|
||||
);
|
||||
|
||||
const ConfirmDescription: React.FunctionComponent = ({}) => (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.deleteFleetProxy.confirmModalText"
|
||||
defaultMessage="This action will change agent policies currently using that proxy. Are you sure you wish to continue?"
|
||||
/>
|
||||
);
|
||||
|
||||
export function useDeleteProxy(onSuccess: () => void) {
|
||||
const { confirm } = useConfirmModal();
|
||||
const { notifications } = useStartServices();
|
||||
const deleteFleetProxy = useCallback(
|
||||
async (fleetProxy: FleetProxy) => {
|
||||
try {
|
||||
const isConfirmed = await confirm(<ConfirmTitle />, <ConfirmDescription />, {
|
||||
buttonColor: 'danger',
|
||||
confirmButtonText: i18n.translate(
|
||||
'xpack.fleet.settings.deleteFleetProxy.confirmButtonLabel',
|
||||
{
|
||||
defaultMessage: 'Delete and deploy changes',
|
||||
}
|
||||
),
|
||||
});
|
||||
|
||||
if (!isConfirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await sendDeleteFleetProxy(fleetProxy.id);
|
||||
|
||||
if (res.error) {
|
||||
throw res.error;
|
||||
}
|
||||
|
||||
onSuccess();
|
||||
} catch (err) {
|
||||
notifications.toasts.addError(err, {
|
||||
title: i18n.translate('xpack.fleet.settings.deleteFleetProxy.errorToastTitle', {
|
||||
defaultMessage: 'Error deleting proxy',
|
||||
}),
|
||||
});
|
||||
}
|
||||
},
|
||||
[confirm, notifications.toasts, onSuccess]
|
||||
);
|
||||
|
||||
return { deleteFleetProxy };
|
||||
}
|
|
@ -15,6 +15,7 @@ import {
|
|||
useGetDownloadSources,
|
||||
useGetFleetServerHosts,
|
||||
useFlyoutContext,
|
||||
useGetFleetProxies,
|
||||
} from '../../hooks';
|
||||
import { FLEET_ROUTING_PATHS, pagePathGetters } from '../../constants';
|
||||
import { DefaultLayout } from '../../layouts';
|
||||
|
@ -26,39 +27,51 @@ import { SettingsPage } from './components/settings_page';
|
|||
import { withConfirmModalProvider } from './hooks/use_confirm_modal';
|
||||
import { FleetServerHostsFlyout } from './components/fleet_server_hosts_flyout';
|
||||
import { EditOutputFlyout } from './components/edit_output_flyout';
|
||||
import { useDeleteOutput } from './hooks/use_delete_output';
|
||||
import { useDeleteFleetServerHost } from './hooks/use_delete_fleet_server_host';
|
||||
import { useDeleteOutput, useDeleteFleetServerHost, useDeleteProxy } from './hooks';
|
||||
import { EditDownloadSourceFlyout } from './components/download_source_flyout';
|
||||
import { useDeleteDownloadSource } from './components/download_source_flyout/use_delete_download_source';
|
||||
import { FleetProxyFlyout } from './components/edit_fleet_proxy_flyout';
|
||||
|
||||
function useSettingsAppData() {
|
||||
const outputs = useGetOutputs();
|
||||
const fleetServerHosts = useGetFleetServerHosts();
|
||||
const downloadSources = useGetDownloadSources();
|
||||
const proxies = useGetFleetProxies();
|
||||
|
||||
return { outputs, fleetServerHosts, downloadSources, proxies };
|
||||
}
|
||||
|
||||
export const SettingsApp = withConfirmModalProvider(() => {
|
||||
useBreadcrumbs('settings');
|
||||
const history = useHistory();
|
||||
|
||||
const outputs = useGetOutputs();
|
||||
const fleetServerHosts = useGetFleetServerHosts();
|
||||
const downloadSources = useGetDownloadSources();
|
||||
const flyoutContext = useFlyoutContext();
|
||||
|
||||
const { outputs, fleetServerHosts, downloadSources, proxies } = useSettingsAppData();
|
||||
|
||||
const { deleteOutput } = useDeleteOutput(outputs.resendRequest);
|
||||
const { deleteDownloadSource } = useDeleteDownloadSource(downloadSources.resendRequest);
|
||||
const { deleteFleetServerHost } = useDeleteFleetServerHost(fleetServerHosts.resendRequest);
|
||||
const { deleteFleetProxy } = useDeleteProxy(proxies.resendRequest);
|
||||
|
||||
const resendOutputRequest = outputs.resendRequest;
|
||||
const resendDownloadSourceRequest = downloadSources.resendRequest;
|
||||
const resendFleetServerHostsRequest = fleetServerHosts.resendRequest;
|
||||
const resendProxiesRequest = proxies.resendRequest;
|
||||
|
||||
const onCloseCallback = useCallback(() => {
|
||||
flyoutContext.closeFleetServerFlyout();
|
||||
resendOutputRequest();
|
||||
resendDownloadSourceRequest();
|
||||
resendFleetServerHostsRequest();
|
||||
resendProxiesRequest();
|
||||
history.replace(pagePathGetters.settings()[1]);
|
||||
}, [
|
||||
flyoutContext,
|
||||
resendOutputRequest,
|
||||
resendDownloadSourceRequest,
|
||||
resendFleetServerHostsRequest,
|
||||
resendProxiesRequest,
|
||||
history,
|
||||
]);
|
||||
|
||||
|
@ -68,7 +81,9 @@ export const SettingsApp = withConfirmModalProvider(() => {
|
|||
(fleetServerHosts.isLoading && fleetServerHosts.isInitialRequest) ||
|
||||
!fleetServerHosts.data?.items ||
|
||||
(downloadSources.isLoading && downloadSources.isInitialRequest) ||
|
||||
!downloadSources.data?.items
|
||||
!downloadSources.data?.items ||
|
||||
(proxies.isLoading && proxies.isInitialRequest) ||
|
||||
!proxies.data?.items
|
||||
) {
|
||||
return (
|
||||
<DefaultLayout section="settings">
|
||||
|
@ -93,6 +108,7 @@ export const SettingsApp = withConfirmModalProvider(() => {
|
|||
return (
|
||||
<EuiPortal>
|
||||
<FleetServerHostsFlyout
|
||||
proxies={proxies.data?.items ?? []}
|
||||
onClose={onCloseCallback}
|
||||
fleetServerHost={fleetServerHost}
|
||||
/>
|
||||
|
@ -107,9 +123,29 @@ export const SettingsApp = withConfirmModalProvider(() => {
|
|||
</Route>
|
||||
<Route path={FLEET_ROUTING_PATHS.settings_create_outputs}>
|
||||
<EuiPortal>
|
||||
<EditOutputFlyout onClose={onCloseCallback} />
|
||||
<EditOutputFlyout proxies={proxies.data.items} onClose={onCloseCallback} />
|
||||
</EuiPortal>
|
||||
</Route>
|
||||
<Route path={FLEET_ROUTING_PATHS.settings_create_fleet_proxy}>
|
||||
<EuiPortal>
|
||||
<FleetProxyFlyout onClose={onCloseCallback} />
|
||||
</EuiPortal>
|
||||
</Route>
|
||||
<Route path={FLEET_ROUTING_PATHS.settings_edit_fleet_proxy}>
|
||||
{(route: { match: { params: { itemId: string } } }) => {
|
||||
const fleetProxy = proxies.data?.items.find(
|
||||
(item) => route.match.params.itemId === item.id
|
||||
);
|
||||
if (!fleetProxy) {
|
||||
return <Redirect to={FLEET_ROUTING_PATHS.settings} />;
|
||||
}
|
||||
return (
|
||||
<EuiPortal>
|
||||
<FleetProxyFlyout onClose={onCloseCallback} fleetProxy={fleetProxy} />
|
||||
</EuiPortal>
|
||||
);
|
||||
}}
|
||||
</Route>
|
||||
<Route path={FLEET_ROUTING_PATHS.settings_edit_outputs}>
|
||||
{(route: { match: { params: { outputId: string } } }) => {
|
||||
const output = outputs.data?.items.find((o) => route.match.params.outputId === o.id);
|
||||
|
@ -119,7 +155,11 @@ export const SettingsApp = withConfirmModalProvider(() => {
|
|||
|
||||
return (
|
||||
<EuiPortal>
|
||||
<EditOutputFlyout onClose={onCloseCallback} output={output} />
|
||||
<EditOutputFlyout
|
||||
proxies={proxies.data?.items ?? []}
|
||||
onClose={onCloseCallback}
|
||||
output={output}
|
||||
/>
|
||||
</EuiPortal>
|
||||
);
|
||||
}}
|
||||
|
@ -151,6 +191,8 @@ export const SettingsApp = withConfirmModalProvider(() => {
|
|||
</Switch>
|
||||
</Router>
|
||||
<SettingsPage
|
||||
deleteFleetProxy={deleteFleetProxy}
|
||||
proxies={proxies.data.items}
|
||||
outputs={outputs.data.items}
|
||||
fleetServerHosts={fleetServerHosts.data.items}
|
||||
deleteOutput={deleteOutput}
|
||||
|
|
|
@ -19,6 +19,7 @@ export type StaticPage =
|
|||
| 'settings_create_outputs'
|
||||
| 'settings_create_download_sources'
|
||||
| 'settings_create_fleet_server_hosts'
|
||||
| 'settings_create_fleet_proxy'
|
||||
| 'debug';
|
||||
|
||||
export type DynamicPage =
|
||||
|
@ -44,7 +45,8 @@ export type DynamicPage =
|
|||
| 'agent_details_diagnostics'
|
||||
| 'settings_edit_outputs'
|
||||
| 'settings_edit_download_sources'
|
||||
| 'settings_edit_fleet_server_hosts';
|
||||
| 'settings_edit_fleet_server_hosts'
|
||||
| 'settings_edit_fleet_proxy';
|
||||
|
||||
export type Page = StaticPage | DynamicPage;
|
||||
|
||||
|
@ -77,6 +79,8 @@ export const FLEET_ROUTING_PATHS = {
|
|||
settings_create_outputs: '/settings/create-outputs',
|
||||
settings_edit_outputs: '/settings/outputs/:outputId',
|
||||
settings_create_download_sources: '/settings/create-download-sources',
|
||||
settings_create_fleet_proxy: '/settings/create-fleet-proxy',
|
||||
settings_edit_fleet_proxy: '/settings/fleet-proxies/:itemId',
|
||||
settings_edit_download_sources: '/settings/downloadSources/:downloadSourceId',
|
||||
debug: '/_debug',
|
||||
|
||||
|
@ -213,6 +217,14 @@ export const pagePathGetters: {
|
|||
FLEET_BASE_PATH,
|
||||
FLEET_ROUTING_PATHS.settings_create_fleet_server_hosts,
|
||||
],
|
||||
settings_create_fleet_proxy: () => [
|
||||
FLEET_BASE_PATH,
|
||||
FLEET_ROUTING_PATHS.settings_create_fleet_proxy,
|
||||
],
|
||||
settings_edit_fleet_proxy: ({ itemId }) => [
|
||||
FLEET_BASE_PATH,
|
||||
FLEET_ROUTING_PATHS.settings_edit_fleet_proxy.replace(':itemId', itemId.toString()),
|
||||
],
|
||||
settings_edit_outputs: ({ outputId }) => [
|
||||
FLEET_BASE_PATH,
|
||||
FLEET_ROUTING_PATHS.settings_edit_outputs.replace(':outputId', outputId as string),
|
||||
|
|
|
@ -9,6 +9,18 @@ import { useState, useCallback, useEffect } from 'react';
|
|||
import type React from 'react';
|
||||
import type { EuiSwitchEvent } from '@elastic/eui';
|
||||
|
||||
export interface FormInput {
|
||||
validate: () => boolean;
|
||||
}
|
||||
|
||||
export function validateInputs(inputs: { [k: string]: FormInput }) {
|
||||
return Object.values(inputs).reduce((acc, input) => {
|
||||
const res = input.validate();
|
||||
|
||||
return acc === false ? acc : res;
|
||||
}, true);
|
||||
}
|
||||
|
||||
export function useInput(
|
||||
defaultValue = '',
|
||||
validate?: (value: string) => string[] | undefined,
|
||||
|
@ -88,6 +100,8 @@ export function useSwitchInput(defaultValue = false, disabled = false) {
|
|||
setValue(newValue);
|
||||
};
|
||||
|
||||
const validate = useCallback(() => true, []);
|
||||
|
||||
return {
|
||||
value,
|
||||
props: {
|
||||
|
@ -95,6 +109,7 @@ export function useSwitchInput(defaultValue = false, disabled = false) {
|
|||
checked: value,
|
||||
disabled,
|
||||
},
|
||||
validate,
|
||||
formRowProps: {},
|
||||
setValue,
|
||||
hasChanged,
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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 { fleetProxiesRoutesService } from '../../../common/services';
|
||||
import type {
|
||||
GetFleetProxiesResponse,
|
||||
PostFleetProxiesRequest,
|
||||
PutFleetProxiesRequest,
|
||||
} from '../../../common/types/rest_spec/fleet_proxies';
|
||||
|
||||
import { sendRequest, useRequest } from './use_request';
|
||||
|
||||
export function useGetFleetProxies() {
|
||||
return useRequest<GetFleetProxiesResponse>({
|
||||
method: 'get',
|
||||
path: fleetProxiesRoutesService.getListPath(),
|
||||
});
|
||||
}
|
||||
|
||||
export function sendDeleteFleetProxy(proxyId: string) {
|
||||
return sendRequest({ method: 'delete', path: fleetProxiesRoutesService.getDeletePath(proxyId) });
|
||||
}
|
||||
|
||||
export function sendPostFleetProxy(body: PostFleetProxiesRequest['body']) {
|
||||
return sendRequest({ method: 'post', path: fleetProxiesRoutesService.getCreatePath(), body });
|
||||
}
|
||||
|
||||
export function sendPutFleetProxy(proxyId: string, body: PutFleetProxiesRequest['body']) {
|
||||
return sendRequest({
|
||||
method: 'put',
|
||||
path: fleetProxiesRoutesService.getUpdatePath(proxyId),
|
||||
body,
|
||||
});
|
||||
}
|
|
@ -19,3 +19,4 @@ export * from './app';
|
|||
export * from './ingest_pipelines';
|
||||
export * from './download_source';
|
||||
export * from './fleet_server_hosts';
|
||||
export * from './fleet_proxies';
|
||||
|
|
|
@ -25,6 +25,7 @@ export type {
|
|||
Output,
|
||||
DownloadSource,
|
||||
FleetServerHost,
|
||||
FleetProxy,
|
||||
DataStream,
|
||||
Settings,
|
||||
ActionStatus,
|
||||
|
|
|
@ -67,6 +67,8 @@ export {
|
|||
// Fleet server host
|
||||
DEFAULT_FLEET_SERVER_HOST_ID,
|
||||
FLEET_SERVER_HOST_SAVED_OBJECT_TYPE,
|
||||
// Proxy
|
||||
FLEET_PROXY_SAVED_OBJECT_TYPE,
|
||||
// Authz
|
||||
ENDPOINT_PRIVILEGES,
|
||||
} from '../../common/constants';
|
||||
|
|
|
@ -77,6 +77,7 @@ export class OutputLicenceError extends FleetError {}
|
|||
export class DownloadSourceError extends FleetError {}
|
||||
|
||||
export class FleetServerHostUnauthorizedError extends FleetError {}
|
||||
export class FleetProxyUnauthorizedError extends FleetError {}
|
||||
|
||||
export class ArtifactsClientError extends FleetError {}
|
||||
export class ArtifactsClientAccessDeniedError extends FleetError {
|
||||
|
|
|
@ -67,22 +67,7 @@ import {
|
|||
FLEET_SERVER_HOST_SAVED_OBJECT_TYPE,
|
||||
} from './constants';
|
||||
import { registerSavedObjects, registerEncryptedSavedObjects } from './saved_objects';
|
||||
import {
|
||||
registerEPMRoutes,
|
||||
registerPackagePolicyRoutes,
|
||||
registerDataStreamRoutes,
|
||||
registerAgentPolicyRoutes,
|
||||
registerSetupRoutes,
|
||||
registerAgentAPIRoutes,
|
||||
registerEnrollmentApiKeyRoutes,
|
||||
registerOutputRoutes,
|
||||
registerSettingsRoutes,
|
||||
registerAppRoutes,
|
||||
registerPreconfigurationRoutes,
|
||||
registerDownloadSourcesRoutes,
|
||||
registerHealthCheckRoutes,
|
||||
registerFleetServerHostRoutes,
|
||||
} from './routes';
|
||||
import { registerRoutes } from './routes';
|
||||
|
||||
import type { ExternalCallback, FleetRequestHandlerContext } from './types';
|
||||
import type {
|
||||
|
@ -402,29 +387,7 @@ export class FleetPlugin
|
|||
makeRouterWithFleetAuthz(router);
|
||||
|
||||
core.http.registerOnPostAuth(fleetAuthzOnPostAuthHandler);
|
||||
|
||||
// Always register app routes for permissions checking
|
||||
registerAppRoutes(fleetAuthzRouter);
|
||||
|
||||
// The upload package route is only authorized for the superuser
|
||||
registerEPMRoutes(fleetAuthzRouter);
|
||||
|
||||
registerSetupRoutes(fleetAuthzRouter, config);
|
||||
registerAgentPolicyRoutes(fleetAuthzRouter);
|
||||
registerPackagePolicyRoutes(fleetAuthzRouter);
|
||||
registerOutputRoutes(fleetAuthzRouter);
|
||||
registerSettingsRoutes(fleetAuthzRouter);
|
||||
registerDataStreamRoutes(fleetAuthzRouter);
|
||||
registerPreconfigurationRoutes(fleetAuthzRouter);
|
||||
registerFleetServerHostRoutes(fleetAuthzRouter);
|
||||
registerDownloadSourcesRoutes(fleetAuthzRouter);
|
||||
registerHealthCheckRoutes(fleetAuthzRouter);
|
||||
|
||||
// Conditional config routes
|
||||
if (config.agents.enabled) {
|
||||
registerAgentAPIRoutes(fleetAuthzRouter, config);
|
||||
registerEnrollmentApiKeyRoutes(fleetAuthzRouter);
|
||||
}
|
||||
registerRoutes(fleetAuthzRouter, config);
|
||||
|
||||
this.telemetryEventsSender.setup(deps.telemetry);
|
||||
this.bulkActionsResolver = new BulkActionsResolver(deps.taskManager, core);
|
||||
|
|
135
x-pack/plugins/fleet/server/routes/fleet_proxies/handler.ts
Normal file
135
x-pack/plugins/fleet/server/routes/fleet_proxies/handler.ts
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* 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 { RequestHandler } from '@kbn/core/server';
|
||||
import { SavedObjectsErrorHelpers } from '@kbn/core/server';
|
||||
import type { TypeOf } from '@kbn/config-schema';
|
||||
|
||||
import {
|
||||
listFleetProxies,
|
||||
createFleetProxy,
|
||||
deleteFleetProxy,
|
||||
getFleetProxy,
|
||||
updateFleetProxy,
|
||||
} from '../../services/fleet_proxies';
|
||||
import { defaultFleetErrorHandler } from '../../errors';
|
||||
import type {
|
||||
GetOneFleetProxyRequestSchema,
|
||||
PostFleetProxyRequestSchema,
|
||||
PutFleetProxyRequestSchema,
|
||||
} from '../../types';
|
||||
|
||||
export const postFleetProxyHandler: RequestHandler<
|
||||
undefined,
|
||||
undefined,
|
||||
TypeOf<typeof PostFleetProxyRequestSchema.body>
|
||||
> = async (context, request, response) => {
|
||||
const coreContext = await context.core;
|
||||
const soClient = coreContext.savedObjects.client;
|
||||
try {
|
||||
const { id, ...data } = request.body;
|
||||
const proxy = await createFleetProxy(soClient, { ...data, is_preconfigured: false }, { id });
|
||||
|
||||
const body = {
|
||||
item: proxy,
|
||||
};
|
||||
|
||||
return response.ok({ body });
|
||||
} catch (error) {
|
||||
return defaultFleetErrorHandler({ error, response });
|
||||
}
|
||||
};
|
||||
|
||||
export const putFleetProxyHandler: RequestHandler<
|
||||
TypeOf<typeof PutFleetProxyRequestSchema.params>,
|
||||
undefined,
|
||||
TypeOf<typeof PutFleetProxyRequestSchema.body>
|
||||
> = async (context, request, response) => {
|
||||
try {
|
||||
const coreContext = await await context.core;
|
||||
const soClient = coreContext.savedObjects.client;
|
||||
|
||||
const item = await updateFleetProxy(soClient, request.params.itemId, request.body);
|
||||
const body = {
|
||||
item,
|
||||
};
|
||||
|
||||
// TODO bump policies on update
|
||||
|
||||
return response.ok({ body });
|
||||
} catch (error) {
|
||||
if (SavedObjectsErrorHelpers.isNotFoundError(error)) {
|
||||
return response.notFound({
|
||||
body: { message: `Proxy ${request.params.itemId} not found` },
|
||||
});
|
||||
}
|
||||
|
||||
return defaultFleetErrorHandler({ error, response });
|
||||
}
|
||||
};
|
||||
|
||||
export const getAllFleetProxyHandler: RequestHandler = async (context, request, response) => {
|
||||
const soClient = (await context.core).savedObjects.client;
|
||||
try {
|
||||
const res = await listFleetProxies(soClient);
|
||||
const body = {
|
||||
items: res.items,
|
||||
page: res.page,
|
||||
perPage: res.perPage,
|
||||
total: res.total,
|
||||
};
|
||||
|
||||
return response.ok({ body });
|
||||
} catch (error) {
|
||||
return defaultFleetErrorHandler({ error, response });
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteFleetProxyHandler: RequestHandler<
|
||||
TypeOf<typeof GetOneFleetProxyRequestSchema.params>
|
||||
> = async (context, request, response) => {
|
||||
try {
|
||||
const coreContext = await context.core;
|
||||
const soClient = coreContext.savedObjects.client;
|
||||
await deleteFleetProxy(soClient, request.params.itemId);
|
||||
const body = {
|
||||
id: request.params.itemId,
|
||||
};
|
||||
|
||||
return response.ok({ body });
|
||||
} catch (error) {
|
||||
if (SavedObjectsErrorHelpers.isNotFoundError(error)) {
|
||||
return response.notFound({
|
||||
body: { message: `Fleet proxy ${request.params.itemId} not found` },
|
||||
});
|
||||
}
|
||||
|
||||
return defaultFleetErrorHandler({ error, response });
|
||||
}
|
||||
};
|
||||
|
||||
export const getFleetProxyHandler: RequestHandler<
|
||||
TypeOf<typeof GetOneFleetProxyRequestSchema.params>
|
||||
> = async (context, request, response) => {
|
||||
const soClient = (await context.core).savedObjects.client;
|
||||
try {
|
||||
const item = await getFleetProxy(soClient, request.params.itemId);
|
||||
const body = {
|
||||
item,
|
||||
};
|
||||
|
||||
return response.ok({ body });
|
||||
} catch (error) {
|
||||
if (SavedObjectsErrorHelpers.isNotFoundError(error)) {
|
||||
return response.notFound({
|
||||
body: { message: `Fleet proxy ${request.params.itemId} not found` },
|
||||
});
|
||||
}
|
||||
|
||||
return defaultFleetErrorHandler({ error, response });
|
||||
}
|
||||
};
|
79
x-pack/plugins/fleet/server/routes/fleet_proxies/index.ts
Normal file
79
x-pack/plugins/fleet/server/routes/fleet_proxies/index.ts
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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 { FLEET_PROXY_API_ROUTES } from '../../../common/constants';
|
||||
import {
|
||||
GetOneFleetProxyRequestSchema,
|
||||
PostFleetProxyRequestSchema,
|
||||
PutFleetProxyRequestSchema,
|
||||
} from '../../types';
|
||||
|
||||
import type { FleetAuthzRouter } from '../security';
|
||||
|
||||
import {
|
||||
getAllFleetProxyHandler,
|
||||
postFleetProxyHandler,
|
||||
deleteFleetProxyHandler,
|
||||
getFleetProxyHandler,
|
||||
putFleetProxyHandler,
|
||||
} from './handler';
|
||||
|
||||
export const registerRoutes = (router: FleetAuthzRouter) => {
|
||||
router.get(
|
||||
{
|
||||
path: FLEET_PROXY_API_ROUTES.LIST_PATTERN,
|
||||
validate: false,
|
||||
fleetAuthz: {
|
||||
fleet: { all: true },
|
||||
},
|
||||
},
|
||||
getAllFleetProxyHandler
|
||||
);
|
||||
|
||||
router.post(
|
||||
{
|
||||
path: FLEET_PROXY_API_ROUTES.CREATE_PATTERN,
|
||||
validate: PostFleetProxyRequestSchema,
|
||||
fleetAuthz: {
|
||||
fleet: { all: true },
|
||||
},
|
||||
},
|
||||
postFleetProxyHandler
|
||||
);
|
||||
|
||||
router.put(
|
||||
{
|
||||
path: FLEET_PROXY_API_ROUTES.UPDATE_PATTERN,
|
||||
validate: PutFleetProxyRequestSchema,
|
||||
fleetAuthz: {
|
||||
fleet: { all: true },
|
||||
},
|
||||
},
|
||||
putFleetProxyHandler
|
||||
);
|
||||
|
||||
router.get(
|
||||
{
|
||||
path: FLEET_PROXY_API_ROUTES.DELETE_PATTERN,
|
||||
validate: GetOneFleetProxyRequestSchema,
|
||||
fleetAuthz: {
|
||||
fleet: { all: true },
|
||||
},
|
||||
},
|
||||
getFleetProxyHandler
|
||||
);
|
||||
|
||||
router.delete(
|
||||
{
|
||||
path: FLEET_PROXY_API_ROUTES.DELETE_PATTERN,
|
||||
validate: GetOneFleetProxyRequestSchema,
|
||||
fleetAuthz: {
|
||||
fleet: { all: true },
|
||||
},
|
||||
},
|
||||
deleteFleetProxyHandler
|
||||
);
|
||||
};
|
|
@ -5,17 +5,47 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export { registerRoutes as registerAgentPolicyRoutes } from './agent_policy';
|
||||
export { registerRoutes as registerPackagePolicyRoutes } from './package_policy';
|
||||
export { registerRoutes as registerDataStreamRoutes } from './data_streams';
|
||||
export { registerRoutes as registerEPMRoutes } from './epm';
|
||||
export { registerRoutes as registerSetupRoutes } from './setup';
|
||||
export { registerAPIRoutes as registerAgentAPIRoutes } from './agent';
|
||||
export { registerRoutes as registerEnrollmentApiKeyRoutes } from './enrollment_api_key';
|
||||
export { registerRoutes as registerOutputRoutes } from './output';
|
||||
export { registerRoutes as registerSettingsRoutes } from './settings';
|
||||
export { registerRoutes as registerAppRoutes } from './app';
|
||||
export { registerRoutes as registerPreconfigurationRoutes } from './preconfiguration';
|
||||
export { registerRoutes as registerDownloadSourcesRoutes } from './download_source';
|
||||
export { registerRoutes as registerHealthCheckRoutes } from './health_check';
|
||||
export { registerRoutes as registerFleetServerHostRoutes } from './fleet_server_policy_config';
|
||||
import type { FleetConfigType } from '../config';
|
||||
|
||||
import { registerRoutes as registerAgentPolicyRoutes } from './agent_policy';
|
||||
import { registerRoutes as registerPackagePolicyRoutes } from './package_policy';
|
||||
import { registerRoutes as registerDataStreamRoutes } from './data_streams';
|
||||
import { registerRoutes as registerEPMRoutes } from './epm';
|
||||
import { registerRoutes as registerSetupRoutes } from './setup';
|
||||
import { registerAPIRoutes as registerAgentAPIRoutes } from './agent';
|
||||
import { registerRoutes as registerEnrollmentApiKeyRoutes } from './enrollment_api_key';
|
||||
import { registerRoutes as registerOutputRoutes } from './output';
|
||||
import { registerRoutes as registerSettingsRoutes } from './settings';
|
||||
import { registerRoutes as registerAppRoutes } from './app';
|
||||
import { registerRoutes as registerPreconfigurationRoutes } from './preconfiguration';
|
||||
import { registerRoutes as registerDownloadSourcesRoutes } from './download_source';
|
||||
import { registerRoutes as registerHealthCheckRoutes } from './health_check';
|
||||
import { registerRoutes as registerFleetServerHostRoutes } from './fleet_server_policy_config';
|
||||
import { registerRoutes as registerFleetProxiesRoutes } from './fleet_proxies';
|
||||
import type { FleetAuthzRouter } from './security';
|
||||
|
||||
export async function registerRoutes(fleetAuthzRouter: FleetAuthzRouter, config: FleetConfigType) {
|
||||
// Always register app routes for permissions checking
|
||||
registerAppRoutes(fleetAuthzRouter);
|
||||
|
||||
// The upload package route is only authorized for the superuser
|
||||
registerEPMRoutes(fleetAuthzRouter);
|
||||
|
||||
registerSetupRoutes(fleetAuthzRouter, config);
|
||||
registerAgentPolicyRoutes(fleetAuthzRouter);
|
||||
registerPackagePolicyRoutes(fleetAuthzRouter);
|
||||
registerOutputRoutes(fleetAuthzRouter);
|
||||
registerSettingsRoutes(fleetAuthzRouter);
|
||||
registerDataStreamRoutes(fleetAuthzRouter);
|
||||
registerPreconfigurationRoutes(fleetAuthzRouter);
|
||||
registerFleetServerHostRoutes(fleetAuthzRouter);
|
||||
registerFleetProxiesRoutes(fleetAuthzRouter);
|
||||
registerDownloadSourcesRoutes(fleetAuthzRouter);
|
||||
registerHealthCheckRoutes(fleetAuthzRouter);
|
||||
|
||||
// Conditional config routes
|
||||
if (config.agents.enabled) {
|
||||
registerAgentAPIRoutes(fleetAuthzRouter, config);
|
||||
registerEnrollmentApiKeyRoutes(fleetAuthzRouter);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE,
|
||||
DOWNLOAD_SOURCE_SAVED_OBJECT_TYPE,
|
||||
FLEET_SERVER_HOST_SAVED_OBJECT_TYPE,
|
||||
FLEET_PROXY_SAVED_OBJECT_TYPE,
|
||||
} from '../constants';
|
||||
|
||||
import {
|
||||
|
@ -136,6 +137,7 @@ const getSavedObjectTypes = (
|
|||
config_yaml: { type: 'text' },
|
||||
is_preconfigured: { type: 'boolean', index: false },
|
||||
ssl: { type: 'binary' },
|
||||
proxy_id: { type: 'keyword' },
|
||||
},
|
||||
},
|
||||
migrations: {
|
||||
|
@ -362,6 +364,26 @@ const getSavedObjectTypes = (
|
|||
is_default: { type: 'boolean' },
|
||||
host_urls: { type: 'keyword', index: false },
|
||||
is_preconfigured: { type: 'boolean' },
|
||||
proxy_id: { type: 'keyword' },
|
||||
},
|
||||
},
|
||||
},
|
||||
[FLEET_PROXY_SAVED_OBJECT_TYPE]: {
|
||||
name: FLEET_PROXY_SAVED_OBJECT_TYPE,
|
||||
hidden: false,
|
||||
namespaceType: 'agnostic',
|
||||
management: {
|
||||
importableAndExportable: false,
|
||||
},
|
||||
mappings: {
|
||||
properties: {
|
||||
name: { type: 'keyword' },
|
||||
url: { type: 'keyword', index: false },
|
||||
proxy_headers: { type: 'text', index: false },
|
||||
certificate_authorities: { type: 'keyword', index: false },
|
||||
certificate: { type: 'keyword', index: false },
|
||||
certificate_key: { type: 'keyword', index: false },
|
||||
is_preconfigured: { type: 'boolean' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -395,6 +417,7 @@ export function registerEncryptedSavedObjects(
|
|||
'config',
|
||||
'config_yaml',
|
||||
'is_preconfigured',
|
||||
'proxy_id',
|
||||
]),
|
||||
});
|
||||
// Encrypted saved objects
|
||||
|
|
149
x-pack/plugins/fleet/server/services/fleet_proxies.ts
Normal file
149
x-pack/plugins/fleet/server/services/fleet_proxies.ts
Normal file
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* 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 { SavedObjectsClientContract, SavedObject } from '@kbn/core/server';
|
||||
|
||||
import { FLEET_PROXY_SAVED_OBJECT_TYPE, SO_SEARCH_LIMIT } from '../constants';
|
||||
import { FleetProxyUnauthorizedError } from '../errors';
|
||||
import type { FleetProxy, FleetProxySOAttributes, NewFleetProxy } from '../types';
|
||||
|
||||
function savedObjectToFleetProxy(so: SavedObject<FleetProxySOAttributes>): FleetProxy {
|
||||
const { proxy_headers: proxyHeaders, ...rest } = so.attributes;
|
||||
|
||||
return {
|
||||
id: so.id,
|
||||
proxy_headers: proxyHeaders && proxyHeaders !== '' ? JSON.parse(proxyHeaders) : undefined,
|
||||
...rest,
|
||||
};
|
||||
}
|
||||
|
||||
function fleetProxyDataToSOAttribute(data: NewFleetProxy): FleetProxySOAttributes;
|
||||
function fleetProxyDataToSOAttribute(data: Partial<NewFleetProxy>): Partial<FleetProxySOAttributes>;
|
||||
function fleetProxyDataToSOAttribute(
|
||||
data: Partial<NewFleetProxy> | NewFleetProxy
|
||||
): Partial<FleetProxySOAttributes> | Partial<FleetProxySOAttributes> {
|
||||
const { proxy_headers: proxyHeaders, ...rest } = data;
|
||||
|
||||
return {
|
||||
proxy_headers: proxyHeaders ? JSON.stringify(proxyHeaders) : null,
|
||||
...rest,
|
||||
};
|
||||
}
|
||||
|
||||
export async function listFleetProxies(soClient: SavedObjectsClientContract) {
|
||||
const res = await soClient.find<FleetProxySOAttributes>({
|
||||
type: FLEET_PROXY_SAVED_OBJECT_TYPE,
|
||||
perPage: SO_SEARCH_LIMIT,
|
||||
});
|
||||
|
||||
return {
|
||||
items: res.saved_objects.map<FleetProxy>(savedObjectToFleetProxy),
|
||||
total: res.total,
|
||||
page: res.page,
|
||||
perPage: res.per_page,
|
||||
};
|
||||
}
|
||||
|
||||
export async function createFleetProxy(
|
||||
soClient: SavedObjectsClientContract,
|
||||
data: NewFleetProxy,
|
||||
options?: { id?: string; overwrite?: boolean; fromPreconfiguration?: boolean }
|
||||
): Promise<FleetProxy> {
|
||||
const res = await soClient.create<FleetProxySOAttributes>(
|
||||
FLEET_PROXY_SAVED_OBJECT_TYPE,
|
||||
fleetProxyDataToSOAttribute(data),
|
||||
{
|
||||
id: options?.id,
|
||||
overwrite: options?.overwrite,
|
||||
}
|
||||
);
|
||||
|
||||
return savedObjectToFleetProxy(res);
|
||||
}
|
||||
|
||||
export async function getFleetProxy(
|
||||
soClient: SavedObjectsClientContract,
|
||||
id: string
|
||||
): Promise<FleetProxy> {
|
||||
const res = await soClient.get<FleetProxySOAttributes>(FLEET_PROXY_SAVED_OBJECT_TYPE, id);
|
||||
|
||||
return savedObjectToFleetProxy(res);
|
||||
}
|
||||
|
||||
export async function deleteFleetProxy(
|
||||
soClient: SavedObjectsClientContract,
|
||||
id: string,
|
||||
options?: { fromPreconfiguration?: boolean }
|
||||
) {
|
||||
const fleetServerHost = await getFleetProxy(soClient, id);
|
||||
|
||||
if (fleetServerHost.is_preconfigured && !options?.fromPreconfiguration) {
|
||||
throw new FleetProxyUnauthorizedError(`Cannot delete ${id} preconfigured proxy`);
|
||||
}
|
||||
|
||||
// TODO remove from all outputs and fleet server
|
||||
// await agentPolicyService.removeFleetServerHostFromAll(soClient, esClient, id);
|
||||
|
||||
return await soClient.delete(FLEET_PROXY_SAVED_OBJECT_TYPE, id);
|
||||
}
|
||||
|
||||
export async function updateFleetProxy(
|
||||
soClient: SavedObjectsClientContract,
|
||||
id: string,
|
||||
data: Partial<FleetProxy>,
|
||||
options?: { fromPreconfiguration?: boolean }
|
||||
) {
|
||||
const originalItem = await getFleetProxy(soClient, id);
|
||||
|
||||
if (data.is_preconfigured && !options?.fromPreconfiguration) {
|
||||
throw new FleetProxyUnauthorizedError(`Cannot update ${id} preconfigured proxy`);
|
||||
}
|
||||
|
||||
await soClient.update<FleetProxySOAttributes>(
|
||||
FLEET_PROXY_SAVED_OBJECT_TYPE,
|
||||
id,
|
||||
fleetProxyDataToSOAttribute(data)
|
||||
);
|
||||
|
||||
return {
|
||||
...originalItem,
|
||||
...data,
|
||||
};
|
||||
}
|
||||
|
||||
export async function bulkGetFleetProxies(
|
||||
soClient: SavedObjectsClientContract,
|
||||
ids: string[],
|
||||
{ ignoreNotFound = false } = { ignoreNotFound: true }
|
||||
) {
|
||||
if (ids.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const res = await soClient.bulkGet<FleetProxySOAttributes>(
|
||||
ids.map((id) => ({
|
||||
id,
|
||||
type: FLEET_PROXY_SAVED_OBJECT_TYPE,
|
||||
}))
|
||||
);
|
||||
|
||||
return res.saved_objects
|
||||
.map((so) => {
|
||||
if (so.error) {
|
||||
if (!ignoreNotFound || so.error.statusCode !== 404) {
|
||||
throw so.error;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return savedObjectToFleetProxy(so);
|
||||
})
|
||||
.filter(
|
||||
(fleetProxyOrUndefined): fleetProxyOrUndefined is FleetProxy =>
|
||||
typeof fleetProxyOrUndefined !== 'undefined'
|
||||
);
|
||||
}
|
|
@ -5,7 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server';
|
||||
import type {
|
||||
ElasticsearchClient,
|
||||
SavedObjectsClientContract,
|
||||
SavedObject,
|
||||
} from '@kbn/core/server';
|
||||
|
||||
import { normalizeHostsForAgents } from '../../common/services';
|
||||
import {
|
||||
|
@ -26,6 +30,16 @@ import { FleetServerHostUnauthorizedError } from '../errors';
|
|||
|
||||
import { agentPolicyService } from './agent_policy';
|
||||
|
||||
function savedObjectToFleetServerHost(so: SavedObject<FleetServerHostSOAttributes>) {
|
||||
const data = { ...so.attributes };
|
||||
|
||||
if (data.proxy_id === null) {
|
||||
delete data.proxy_id;
|
||||
}
|
||||
|
||||
return { id: so.id, ...data };
|
||||
}
|
||||
|
||||
export async function createFleetServerHost(
|
||||
soClient: SavedObjectsClientContract,
|
||||
data: NewFleetServerHost,
|
||||
|
@ -53,10 +67,7 @@ export async function createFleetServerHost(
|
|||
{ id: options?.id, overwrite: options?.overwrite }
|
||||
);
|
||||
|
||||
return {
|
||||
id: res.id,
|
||||
...res.attributes,
|
||||
};
|
||||
return savedObjectToFleetServerHost(res);
|
||||
}
|
||||
|
||||
export async function getFleetServerHost(
|
||||
|
@ -68,10 +79,7 @@ export async function getFleetServerHost(
|
|||
id
|
||||
);
|
||||
|
||||
return {
|
||||
id: res.id,
|
||||
...res.attributes,
|
||||
};
|
||||
return savedObjectToFleetServerHost(res);
|
||||
}
|
||||
|
||||
export async function listFleetServerHosts(soClient: SavedObjectsClientContract) {
|
||||
|
@ -81,10 +89,7 @@ export async function listFleetServerHosts(soClient: SavedObjectsClientContract)
|
|||
});
|
||||
|
||||
return {
|
||||
items: res.saved_objects.map<FleetServerHost>((so) => ({
|
||||
id: so.id,
|
||||
...so.attributes,
|
||||
})),
|
||||
items: res.saved_objects.map<FleetServerHost>(savedObjectToFleetServerHost),
|
||||
total: res.total,
|
||||
page: res.page,
|
||||
perPage: res.per_page,
|
||||
|
@ -181,10 +186,7 @@ export async function bulkGetFleetServerHosts(
|
|||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
id: so.id,
|
||||
...so.attributes,
|
||||
};
|
||||
return savedObjectToFleetServerHost(so);
|
||||
})
|
||||
.filter(
|
||||
(fleetServerHostOrUndefined): fleetServerHostOrUndefined is FleetServerHost =>
|
||||
|
@ -223,10 +225,7 @@ export async function getDefaultFleetServerHost(
|
|||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
id: res.saved_objects[0].id,
|
||||
...res.saved_objects[0].attributes,
|
||||
};
|
||||
return savedObjectToFleetServerHost(res.saved_objects[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -65,13 +65,14 @@ export function outputIdToUuid(id: string) {
|
|||
return uuid(id, uuid.DNS);
|
||||
}
|
||||
|
||||
function outputSavedObjectToOutput(so: SavedObject<OutputSOAttributes>) {
|
||||
const { output_id: outputId, ssl, ...atributes } = so.attributes;
|
||||
function outputSavedObjectToOutput(so: SavedObject<OutputSOAttributes>): Output {
|
||||
const { output_id: outputId, ssl, proxy_id: proxyId, ...atributes } = so.attributes;
|
||||
|
||||
return {
|
||||
id: outputId ?? so.id,
|
||||
...atributes,
|
||||
...(ssl ? { ssl: JSON.parse(ssl as string) } : {}),
|
||||
...(proxyId ? { proxy_id: proxyId } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,9 @@ export type {
|
|||
NewFleetServerHost,
|
||||
FleetServerHost,
|
||||
FleetServerHostSOAttributes,
|
||||
NewFleetProxy,
|
||||
FleetProxy,
|
||||
FleetProxySOAttributes,
|
||||
Installation,
|
||||
EpmPackageInstallStatus,
|
||||
InstallationStatus,
|
||||
|
|
|
@ -54,6 +54,7 @@ const OutputBaseSchema = {
|
|||
key: schema.maybe(schema.string()),
|
||||
})
|
||||
),
|
||||
proxy_id: schema.nullable(schema.string()),
|
||||
};
|
||||
|
||||
export const NewOutputSchema = schema.object({ ...OutputBaseSchema });
|
||||
|
@ -81,6 +82,7 @@ export const UpdateOutputSchema = schema.object({
|
|||
key: schema.maybe(schema.string()),
|
||||
})
|
||||
),
|
||||
proxy_id: schema.nullable(schema.string()),
|
||||
});
|
||||
|
||||
export const OutputSchema = schema.object({
|
||||
|
|
46
x-pack/plugins/fleet/server/types/rest_spec/fleet_proxies.ts
Normal file
46
x-pack/plugins/fleet/server/types/rest_spec/fleet_proxies.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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 PostFleetProxyRequestSchema = {
|
||||
body: schema.object({
|
||||
id: schema.maybe(schema.string()),
|
||||
url: schema.string(),
|
||||
name: schema.string(),
|
||||
proxy_headers: schema.maybe(
|
||||
schema.recordOf(
|
||||
schema.string(),
|
||||
schema.oneOf([schema.string(), schema.boolean(), schema.number()])
|
||||
)
|
||||
),
|
||||
certificate_authorities: schema.maybe(schema.string()),
|
||||
certificate: schema.maybe(schema.string()),
|
||||
certificate_key: schema.maybe(schema.string()),
|
||||
}),
|
||||
};
|
||||
|
||||
export const PutFleetProxyRequestSchema = {
|
||||
params: schema.object({ itemId: schema.string() }),
|
||||
body: schema.object({
|
||||
name: schema.maybe(schema.string()),
|
||||
url: schema.maybe(schema.string()),
|
||||
proxy_headers: schema.nullable(
|
||||
schema.recordOf(
|
||||
schema.string(),
|
||||
schema.oneOf([schema.string(), schema.boolean(), schema.number()])
|
||||
)
|
||||
),
|
||||
certificate_authorities: schema.nullable(schema.string()),
|
||||
certificate: schema.nullable(schema.string()),
|
||||
certificate_key: schema.nullable(schema.string()),
|
||||
}),
|
||||
};
|
||||
|
||||
export const GetOneFleetProxyRequestSchema = {
|
||||
params: schema.object({ itemId: schema.string() }),
|
||||
};
|
|
@ -13,6 +13,7 @@ export const PostFleetServerHostRequestSchema = {
|
|||
name: schema.string(),
|
||||
host_urls: schema.arrayOf(schema.string(), { minSize: 1 }),
|
||||
is_default: schema.boolean({ defaultValue: false }),
|
||||
proxy_id: schema.nullable(schema.string()),
|
||||
}),
|
||||
};
|
||||
|
||||
|
@ -26,6 +27,7 @@ export const PutFleetServerHostRequestSchema = {
|
|||
name: schema.maybe(schema.string()),
|
||||
host_urls: schema.maybe(schema.arrayOf(schema.string(), { minSize: 1 })),
|
||||
is_default: schema.maybe(schema.boolean({ defaultValue: false })),
|
||||
proxy_id: schema.nullable(schema.string()),
|
||||
}),
|
||||
};
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ export * from './package_policy';
|
|||
export * from './epm';
|
||||
export * from './enrollment_api_key';
|
||||
export * from './fleet_server_policy_config';
|
||||
export * from './fleet_proxies';
|
||||
export * from './output';
|
||||
export * from './preconfiguration';
|
||||
export * from './settings';
|
||||
|
|
106
x-pack/test/fleet_api_integration/apis/fleet_proxies/crud.ts
Normal file
106
x-pack/test/fleet_api_integration/apis/fleet_proxies/crud.ts
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../../api_integration/ftr_provider_context';
|
||||
import { skipIfNoDockerRegistry } from '../../helpers';
|
||||
import { setupFleetAndAgents } from '../agents/services';
|
||||
|
||||
export default function (providerContext: FtrProviderContext) {
|
||||
const { getService } = providerContext;
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
|
||||
describe('fleet_proxies_crud', async function () {
|
||||
skipIfNoDockerRegistry(providerContext);
|
||||
before(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/fleet/empty_fleet_server');
|
||||
await kibanaServer.savedObjects.cleanStandardList();
|
||||
});
|
||||
setupFleetAndAgents(providerContext);
|
||||
|
||||
const existingId = 'test-default-123';
|
||||
|
||||
before(async function () {
|
||||
await kibanaServer.savedObjects.clean({
|
||||
types: ['fleet-proxy'],
|
||||
});
|
||||
await supertest
|
||||
.post(`/api/fleet/proxies`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
id: existingId,
|
||||
name: 'Test 123',
|
||||
url: 'https://test.fr:3232',
|
||||
})
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await kibanaServer.savedObjects.cleanStandardList();
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/fleet/empty_fleet_server');
|
||||
});
|
||||
|
||||
describe('GET /proxies', () => {
|
||||
it('should list the fleet proxies', async () => {
|
||||
const { body: res } = await supertest.get(`/api/fleet/proxies`).expect(200);
|
||||
|
||||
expect(res.items.length).to.be(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /proxies/{itemId}', () => {
|
||||
it('should return the requested fleet proxy', async () => {
|
||||
const { body: fleetServerHost } = await supertest
|
||||
.get(`/api/fleet/proxies/${existingId}`)
|
||||
.expect(200);
|
||||
|
||||
expect(fleetServerHost).to.eql({
|
||||
item: {
|
||||
id: 'test-default-123',
|
||||
name: 'Test 123',
|
||||
url: 'https://test.fr:3232',
|
||||
is_preconfigured: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a 404 when retrieving a non existing fleet proxy', async function () {
|
||||
await supertest.get(`/api/fleet/proxies/idonotexists`).expect(404);
|
||||
});
|
||||
});
|
||||
|
||||
describe('PUT /proxies/{itemId}', () => {
|
||||
it('should allow to update an existing fleet proxy', async function () {
|
||||
await supertest
|
||||
.put(`/api/fleet/proxies/${existingId}`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
name: 'Test 123 updated',
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const {
|
||||
body: { item: fleetServerHost },
|
||||
} = await supertest.get(`/api/fleet/proxies/${existingId}`).expect(200);
|
||||
|
||||
expect(fleetServerHost.name).to.eql('Test 123 updated');
|
||||
});
|
||||
|
||||
it('should return a 404 when updating a non existing fleet proxy', async function () {
|
||||
await supertest
|
||||
.put(`/api/fleet/proxies/idonotexists`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
name: 'new name',
|
||||
})
|
||||
.expect(404);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -61,5 +61,8 @@ export default function ({ loadTestFile, getService }) {
|
|||
|
||||
// Fleet server hosts
|
||||
loadTestFile(require.resolve('./fleet_server_hosts/crud'));
|
||||
|
||||
// Fleet proxies
|
||||
loadTestFile(require.resolve('./fleet_proxies/crud'));
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue