diff --git a/oas_docs/bundle.json b/oas_docs/bundle.json index 169133c63753c..098fb1de18699 100644 --- a/oas_docs/bundle.json +++ b/oas_docs/bundle.json @@ -9776,6 +9776,191 @@ ] } }, + "/api/fleet/agent_policies/outputs": { + "post": { + "description": "Get list of outputs associated with agent policies", + "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2Foutputs#0", + "parameters": [ + { + "description": "The version of the API to use", + "in": "header", + "name": "elastic-api-version", + "schema": { + "default": "2023-10-31", + "enum": [ + "2023-10-31" + ], + "type": "string" + } + }, + { + "description": "A required header to protect against CSRF attacks", + "in": "header", + "name": "kbn-xsrf", + "required": true, + "schema": { + "example": "true", + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "properties": { + "ids": { + "description": "list of package policy ids", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "ids" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "properties": { + "items": { + "items": { + "additionalProperties": false, + "properties": { + "agentPolicyId": { + "type": "string" + }, + "data": { + "additionalProperties": false, + "properties": { + "integrations": { + "items": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "integrationPolicyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "pkgName": { + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + }, + "output": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ], + "type": "object" + } + }, + "required": [ + "output" + ], + "type": "object" + }, + "monitoring": { + "additionalProperties": false, + "properties": { + "output": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ], + "type": "object" + } + }, + "required": [ + "output" + ], + "type": "object" + } + }, + "required": [ + "monitoring", + "data" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "items" + ], + "type": "object" + } + } + } + }, + "400": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "description": "Generic Error", + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "statusCode": { + "type": "number" + } + }, + "required": [ + "message" + ], + "type": "object" + } + } + } + } + }, + "summary": "", + "tags": [ + "Elastic Agent policies" + ] + } + }, "/api/fleet/agent_policies/{agentPolicyId}": { "get": { "description": "Get an agent policy by ID", @@ -12938,6 +13123,164 @@ ] } }, + "/api/fleet/agent_policies/{agentPolicyId}/outputs": { + "get": { + "description": "Get list of outputs associated with agent policy by policy id", + "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Foutputs#0", + "parameters": [ + { + "description": "The version of the API to use", + "in": "header", + "name": "elastic-api-version", + "schema": { + "default": "2023-10-31", + "enum": [ + "2023-10-31" + ], + "type": "string" + } + }, + { + "in": "path", + "name": "agentPolicyId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "properties": { + "item": { + "additionalProperties": false, + "properties": { + "agentPolicyId": { + "type": "string" + }, + "data": { + "additionalProperties": false, + "properties": { + "integrations": { + "items": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "integrationPolicyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "pkgName": { + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + }, + "output": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ], + "type": "object" + } + }, + "required": [ + "output" + ], + "type": "object" + }, + "monitoring": { + "additionalProperties": false, + "properties": { + "output": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ], + "type": "object" + } + }, + "required": [ + "output" + ], + "type": "object" + } + }, + "required": [ + "monitoring", + "data" + ], + "type": "object" + } + }, + "required": [ + "item" + ], + "type": "object" + } + } + } + }, + "400": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "description": "Generic Error", + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "statusCode": { + "type": "number" + } + }, + "required": [ + "message" + ], + "type": "object" + } + } + } + } + }, + "summary": "", + "tags": [ + "Elastic Agent policies" + ] + } + }, "/api/fleet/agent_status": { "get": { "description": "Get agent status summary", diff --git a/oas_docs/bundle.serverless.json b/oas_docs/bundle.serverless.json index 1d989c69f48d4..479f79922fea8 100644 --- a/oas_docs/bundle.serverless.json +++ b/oas_docs/bundle.serverless.json @@ -9776,6 +9776,191 @@ ] } }, + "/api/fleet/agent_policies/outputs": { + "post": { + "description": "Get list of outputs associated with agent policies", + "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2Foutputs#0", + "parameters": [ + { + "description": "The version of the API to use", + "in": "header", + "name": "elastic-api-version", + "schema": { + "default": "2023-10-31", + "enum": [ + "2023-10-31" + ], + "type": "string" + } + }, + { + "description": "A required header to protect against CSRF attacks", + "in": "header", + "name": "kbn-xsrf", + "required": true, + "schema": { + "example": "true", + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "properties": { + "ids": { + "description": "list of package policy ids", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "ids" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "properties": { + "items": { + "items": { + "additionalProperties": false, + "properties": { + "agentPolicyId": { + "type": "string" + }, + "data": { + "additionalProperties": false, + "properties": { + "integrations": { + "items": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "integrationPolicyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "pkgName": { + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + }, + "output": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ], + "type": "object" + } + }, + "required": [ + "output" + ], + "type": "object" + }, + "monitoring": { + "additionalProperties": false, + "properties": { + "output": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ], + "type": "object" + } + }, + "required": [ + "output" + ], + "type": "object" + } + }, + "required": [ + "monitoring", + "data" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "items" + ], + "type": "object" + } + } + } + }, + "400": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "description": "Generic Error", + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "statusCode": { + "type": "number" + } + }, + "required": [ + "message" + ], + "type": "object" + } + } + } + } + }, + "summary": "", + "tags": [ + "Elastic Agent policies" + ] + } + }, "/api/fleet/agent_policies/{agentPolicyId}": { "get": { "description": "Get an agent policy by ID", @@ -12938,6 +13123,164 @@ ] } }, + "/api/fleet/agent_policies/{agentPolicyId}/outputs": { + "get": { + "description": "Get list of outputs associated with agent policy by policy id", + "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Foutputs#0", + "parameters": [ + { + "description": "The version of the API to use", + "in": "header", + "name": "elastic-api-version", + "schema": { + "default": "2023-10-31", + "enum": [ + "2023-10-31" + ], + "type": "string" + } + }, + { + "in": "path", + "name": "agentPolicyId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "properties": { + "item": { + "additionalProperties": false, + "properties": { + "agentPolicyId": { + "type": "string" + }, + "data": { + "additionalProperties": false, + "properties": { + "integrations": { + "items": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "integrationPolicyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "pkgName": { + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + }, + "output": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ], + "type": "object" + } + }, + "required": [ + "output" + ], + "type": "object" + }, + "monitoring": { + "additionalProperties": false, + "properties": { + "output": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ], + "type": "object" + } + }, + "required": [ + "output" + ], + "type": "object" + } + }, + "required": [ + "monitoring", + "data" + ], + "type": "object" + } + }, + "required": [ + "item" + ], + "type": "object" + } + } + } + }, + "400": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "description": "Generic Error", + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "statusCode": { + "type": "number" + } + }, + "required": [ + "message" + ], + "type": "object" + } + } + } + } + }, + "summary": "", + "tags": [ + "Elastic Agent policies" + ] + } + }, "/api/fleet/agent_status": { "get": { "description": "Get agent status summary", diff --git a/oas_docs/output/kibana.serverless.staging.yaml b/oas_docs/output/kibana.serverless.staging.yaml index e7a3e9c42ec7a..b1f6938936fbd 100644 --- a/oas_docs/output/kibana.serverless.staging.yaml +++ b/oas_docs/output/kibana.serverless.staging.yaml @@ -14594,6 +14594,110 @@ paths: summary: '' tags: - Elastic Agent policies + /api/fleet/agent_policies/{agentPolicyId}/outputs: + get: + description: Get list of outputs associated with agent policy by policy id + operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Foutputs#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - in: path + name: agentPolicyId + required: true + schema: + type: string + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + item: + additionalProperties: false + type: object + properties: + agentPolicyId: + type: string + data: + additionalProperties: false + type: object + properties: + integrations: + items: + additionalProperties: false + type: object + properties: + id: + type: string + integrationPolicyName: + type: string + name: + type: string + pkgName: + type: string + type: array + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + monitoring: + additionalProperties: false + type: object + properties: + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + required: + - monitoring + - data + required: + - item + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + description: Generic Error + type: object + properties: + error: + type: string + message: + type: string + statusCode: + type: number + required: + - message + summary: '' + tags: + - Elastic Agent policies /api/fleet/agent_policies/delete: post: description: Delete agent policy by ID @@ -14664,6 +14768,128 @@ paths: summary: '' tags: - Elastic Agent policies + /api/fleet/agent_policies/outputs: + post: + description: Get list of outputs associated with agent policies + operationId: '%2Fapi%2Ffleet%2Fagent_policies%2Foutputs#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - description: A required header to protect against CSRF attacks + in: header + name: kbn-xsrf + required: true + schema: + example: 'true' + type: string + requestBody: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + ids: + description: list of package policy ids + items: + type: string + type: array + required: + - ids + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + items: + items: + additionalProperties: false + type: object + properties: + agentPolicyId: + type: string + data: + additionalProperties: false + type: object + properties: + integrations: + items: + additionalProperties: false + type: object + properties: + id: + type: string + integrationPolicyName: + type: string + name: + type: string + pkgName: + type: string + type: array + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + monitoring: + additionalProperties: false + type: object + properties: + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + required: + - monitoring + - data + type: array + required: + - items + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + description: Generic Error + type: object + properties: + error: + type: string + message: + type: string + statusCode: + type: number + required: + - message + summary: '' + tags: + - Elastic Agent policies /api/fleet/agent_status: get: description: Get agent status summary diff --git a/oas_docs/output/kibana.serverless.yaml b/oas_docs/output/kibana.serverless.yaml index e7a3e9c42ec7a..b1f6938936fbd 100644 --- a/oas_docs/output/kibana.serverless.yaml +++ b/oas_docs/output/kibana.serverless.yaml @@ -14594,6 +14594,110 @@ paths: summary: '' tags: - Elastic Agent policies + /api/fleet/agent_policies/{agentPolicyId}/outputs: + get: + description: Get list of outputs associated with agent policy by policy id + operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Foutputs#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - in: path + name: agentPolicyId + required: true + schema: + type: string + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + item: + additionalProperties: false + type: object + properties: + agentPolicyId: + type: string + data: + additionalProperties: false + type: object + properties: + integrations: + items: + additionalProperties: false + type: object + properties: + id: + type: string + integrationPolicyName: + type: string + name: + type: string + pkgName: + type: string + type: array + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + monitoring: + additionalProperties: false + type: object + properties: + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + required: + - monitoring + - data + required: + - item + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + description: Generic Error + type: object + properties: + error: + type: string + message: + type: string + statusCode: + type: number + required: + - message + summary: '' + tags: + - Elastic Agent policies /api/fleet/agent_policies/delete: post: description: Delete agent policy by ID @@ -14664,6 +14768,128 @@ paths: summary: '' tags: - Elastic Agent policies + /api/fleet/agent_policies/outputs: + post: + description: Get list of outputs associated with agent policies + operationId: '%2Fapi%2Ffleet%2Fagent_policies%2Foutputs#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - description: A required header to protect against CSRF attacks + in: header + name: kbn-xsrf + required: true + schema: + example: 'true' + type: string + requestBody: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + ids: + description: list of package policy ids + items: + type: string + type: array + required: + - ids + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + items: + items: + additionalProperties: false + type: object + properties: + agentPolicyId: + type: string + data: + additionalProperties: false + type: object + properties: + integrations: + items: + additionalProperties: false + type: object + properties: + id: + type: string + integrationPolicyName: + type: string + name: + type: string + pkgName: + type: string + type: array + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + monitoring: + additionalProperties: false + type: object + properties: + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + required: + - monitoring + - data + type: array + required: + - items + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + description: Generic Error + type: object + properties: + error: + type: string + message: + type: string + statusCode: + type: number + required: + - message + summary: '' + tags: + - Elastic Agent policies /api/fleet/agent_status: get: description: Get agent status summary diff --git a/oas_docs/output/kibana.staging.yaml b/oas_docs/output/kibana.staging.yaml index 16aa969df06d0..ac76216c78801 100644 --- a/oas_docs/output/kibana.staging.yaml +++ b/oas_docs/output/kibana.staging.yaml @@ -18023,6 +18023,110 @@ paths: summary: '' tags: - Elastic Agent policies + /api/fleet/agent_policies/{agentPolicyId}/outputs: + get: + description: Get list of outputs associated with agent policy by policy id + operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Foutputs#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - in: path + name: agentPolicyId + required: true + schema: + type: string + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + item: + additionalProperties: false + type: object + properties: + agentPolicyId: + type: string + data: + additionalProperties: false + type: object + properties: + integrations: + items: + additionalProperties: false + type: object + properties: + id: + type: string + integrationPolicyName: + type: string + name: + type: string + pkgName: + type: string + type: array + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + monitoring: + additionalProperties: false + type: object + properties: + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + required: + - monitoring + - data + required: + - item + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + description: Generic Error + type: object + properties: + error: + type: string + message: + type: string + statusCode: + type: number + required: + - message + summary: '' + tags: + - Elastic Agent policies /api/fleet/agent_policies/delete: post: description: Delete agent policy by ID @@ -18093,6 +18197,128 @@ paths: summary: '' tags: - Elastic Agent policies + /api/fleet/agent_policies/outputs: + post: + description: Get list of outputs associated with agent policies + operationId: '%2Fapi%2Ffleet%2Fagent_policies%2Foutputs#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - description: A required header to protect against CSRF attacks + in: header + name: kbn-xsrf + required: true + schema: + example: 'true' + type: string + requestBody: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + ids: + description: list of package policy ids + items: + type: string + type: array + required: + - ids + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + items: + items: + additionalProperties: false + type: object + properties: + agentPolicyId: + type: string + data: + additionalProperties: false + type: object + properties: + integrations: + items: + additionalProperties: false + type: object + properties: + id: + type: string + integrationPolicyName: + type: string + name: + type: string + pkgName: + type: string + type: array + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + monitoring: + additionalProperties: false + type: object + properties: + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + required: + - monitoring + - data + type: array + required: + - items + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + description: Generic Error + type: object + properties: + error: + type: string + message: + type: string + statusCode: + type: number + required: + - message + summary: '' + tags: + - Elastic Agent policies /api/fleet/agent_status: get: description: Get agent status summary diff --git a/oas_docs/output/kibana.yaml b/oas_docs/output/kibana.yaml index 16aa969df06d0..ac76216c78801 100644 --- a/oas_docs/output/kibana.yaml +++ b/oas_docs/output/kibana.yaml @@ -18023,6 +18023,110 @@ paths: summary: '' tags: - Elastic Agent policies + /api/fleet/agent_policies/{agentPolicyId}/outputs: + get: + description: Get list of outputs associated with agent policy by policy id + operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Foutputs#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - in: path + name: agentPolicyId + required: true + schema: + type: string + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + item: + additionalProperties: false + type: object + properties: + agentPolicyId: + type: string + data: + additionalProperties: false + type: object + properties: + integrations: + items: + additionalProperties: false + type: object + properties: + id: + type: string + integrationPolicyName: + type: string + name: + type: string + pkgName: + type: string + type: array + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + monitoring: + additionalProperties: false + type: object + properties: + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + required: + - monitoring + - data + required: + - item + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + description: Generic Error + type: object + properties: + error: + type: string + message: + type: string + statusCode: + type: number + required: + - message + summary: '' + tags: + - Elastic Agent policies /api/fleet/agent_policies/delete: post: description: Delete agent policy by ID @@ -18093,6 +18197,128 @@ paths: summary: '' tags: - Elastic Agent policies + /api/fleet/agent_policies/outputs: + post: + description: Get list of outputs associated with agent policies + operationId: '%2Fapi%2Ffleet%2Fagent_policies%2Foutputs#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - description: A required header to protect against CSRF attacks + in: header + name: kbn-xsrf + required: true + schema: + example: 'true' + type: string + requestBody: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + ids: + description: list of package policy ids + items: + type: string + type: array + required: + - ids + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + items: + items: + additionalProperties: false + type: object + properties: + agentPolicyId: + type: string + data: + additionalProperties: false + type: object + properties: + integrations: + items: + additionalProperties: false + type: object + properties: + id: + type: string + integrationPolicyName: + type: string + name: + type: string + pkgName: + type: string + type: array + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + monitoring: + additionalProperties: false + type: object + properties: + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + required: + - monitoring + - data + type: array + required: + - items + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + description: Generic Error + type: object + properties: + error: + type: string + message: + type: string + statusCode: + type: number + required: + - message + summary: '' + tags: + - Elastic Agent policies /api/fleet/agent_status: get: description: Get agent status summary diff --git a/x-pack/plugins/fleet/common/constants/routes.ts b/x-pack/plugins/fleet/common/constants/routes.ts index 9b5c35c3b3ce2..c071c6feecbf8 100644 --- a/x-pack/plugins/fleet/common/constants/routes.ts +++ b/x-pack/plugins/fleet/common/constants/routes.ts @@ -81,6 +81,8 @@ export const AGENT_POLICY_API_ROUTES = { DELETE_PATTERN: `${AGENT_POLICY_API_ROOT}/delete`, FULL_INFO_PATTERN: `${AGENT_POLICY_API_ROOT}/{agentPolicyId}/full`, FULL_INFO_DOWNLOAD_PATTERN: `${AGENT_POLICY_API_ROOT}/{agentPolicyId}/download`, + LIST_OUTPUTS_PATTERN: `${AGENT_POLICY_API_ROOT}/outputs`, + INFO_OUTPUTS_PATTERN: `${AGENT_POLICY_API_ROOT}/{agentPolicyId}/outputs`, }; // Kubernetes Manifest API routes diff --git a/x-pack/plugins/fleet/common/services/routes.ts b/x-pack/plugins/fleet/common/services/routes.ts index ff1fb4a5ff693..520a71e1bdc0a 100644 --- a/x-pack/plugins/fleet/common/services/routes.ts +++ b/x-pack/plugins/fleet/common/services/routes.ts @@ -197,6 +197,14 @@ export const agentPolicyRouteService = { getResetAllPreconfiguredAgentPolicyPath: () => { return PRECONFIGURATION_API_ROUTES.RESET_PATTERN; }, + + getInfoOutputsPath: (agentPolicyId: string) => { + return AGENT_POLICY_API_ROUTES.INFO_OUTPUTS_PATTERN.replace('{agentPolicyId}', agentPolicyId); + }, + + getListOutputsPath: () => { + return AGENT_POLICY_API_ROUTES.LIST_OUTPUTS_PATTERN; + }, }; export const dataStreamRouteService = { diff --git a/x-pack/plugins/fleet/common/types/models/agent_policy.ts b/x-pack/plugins/fleet/common/types/models/agent_policy.ts index ebb1aa3afe7f1..ba1a0b182af72 100644 --- a/x-pack/plugins/fleet/common/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/common/types/models/agent_policy.ts @@ -262,3 +262,24 @@ export interface AgentlessApiResponse { id: string; region_id: string; } + +// Definitions for agent policy outputs endpoints +export interface MinimalOutput { + name?: string; + id?: string; +} +export interface IntegrationsOutput extends MinimalOutput { + pkgName?: string; + integrationPolicyName?: string; +} + +export interface OutputsForAgentPolicy { + agentPolicyId?: string; + monitoring: { + output: MinimalOutput; + }; + data: { + output: MinimalOutput; + integrations?: IntegrationsOutput[]; + }; +} diff --git a/x-pack/plugins/fleet/common/types/rest_spec/agent_policy.ts b/x-pack/plugins/fleet/common/types/rest_spec/agent_policy.ts index 42f44f7c7271e..7432d1d00e61e 100644 --- a/x-pack/plugins/fleet/common/types/rest_spec/agent_policy.ts +++ b/x-pack/plugins/fleet/common/types/rest_spec/agent_policy.ts @@ -5,7 +5,12 @@ * 2.0. */ -import type { AgentPolicy, NewAgentPolicy, FullAgentPolicy } from '../models'; +import type { + AgentPolicy, + NewAgentPolicy, + FullAgentPolicy, + OutputsForAgentPolicy, +} from '../models'; import type { ListResult, ListWithKuery, BulkGetResult } from './common'; @@ -93,3 +98,16 @@ export type FetchAllAgentPoliciesOptions = Pick< export type FetchAllAgentPolicyIdsOptions = Pick & { spaceId?: string; }; + +export interface GetAgentPolicyOutputsResponse { + item: OutputsForAgentPolicy; +} +export interface GetListAgentPolicyOutputsResponse { + items: OutputsForAgentPolicy[]; +} + +export interface GetListAgentPolicyOutputsRequest { + body: { + ids?: string[]; + }; +} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details/agent_details_overview.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details/agent_details_overview.tsx index 35fd048cc13cd..2c4113c003841 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details/agent_details_overview.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details/agent_details_overview.tsx @@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage, FormattedRelative } from '@kbn/i18n-react'; import type { Agent, AgentPolicy } from '../../../../../types'; -import { useAgentVersion } from '../../../../../hooks'; +import { useAgentVersion, useGetInfoOutputsForPolicy } from '../../../../../hooks'; import { ExperimentalFeaturesService, isAgentUpgradeable } from '../../../../../services'; import { AgentPolicySummaryLine } from '../../../../../components'; import { AgentHealth } from '../../../components'; @@ -30,6 +30,7 @@ import { Tags } from '../../../components/tags'; import { formatAgentCPU, formatAgentMemory } from '../../../services/agent_metrics'; import { AgentDashboardLink } from '../agent_dashboard_link'; import { AgentUpgradeStatus } from '../../../agent_list_page/components/agent_upgrade_status'; +import { AgentPolicyOutputsSummary } from '../../../agent_list_page/components/agent_policy_outputs_summary'; // Allows child text to be truncated const FlexItemWithMinWidth = styled(EuiFlexItem)` @@ -43,10 +44,17 @@ export const AgentDetailsOverviewSection: React.FunctionComponent<{ const latestAgentVersion = useAgentVersion(); const { displayAgentMetrics } = ExperimentalFeaturesService.get(); + const outputRes = useGetInfoOutputsForPolicy(agentPolicy?.id); + const outputs = outputRes?.data?.item; + return ( - + {displayAgentMetrics && ( @@ -206,6 +214,22 @@ export const AgentDetailsOverviewSection: React.FunctionComponent<{ ? agent.local_metadata.host.id : '-', }, + { + title: i18n.translate('xpack.fleet.agentDetails.outputForMonitoringLabel', { + defaultMessage: 'Output for integrations', + }), + description: outputs ? : '-', + }, + { + title: i18n.translate('xpack.fleet.agentDetails.outputForMonitoringLabel', { + defaultMessage: 'Output for monitoring', + }), + description: outputs ? ( + + ) : ( + '-' + ), + }, { title: i18n.translate('xpack.fleet.agentDetails.logLevel', { defaultMessage: 'Logging level', diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_list_table.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_list_table.tsx index 4c6c83dd7145e..d70ed67247207 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_list_table.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_list_table.tsx @@ -4,7 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React from 'react'; +import React, { useCallback, useMemo } from 'react'; +import type { EuiBasicTableColumn } from '@elastic/eui'; import { type CriteriaWithPagination } from '@elastic/eui'; import { EuiBasicTable, @@ -23,20 +24,31 @@ import { isAgentUpgradeable, ExperimentalFeaturesService } from '../../../../ser import { AgentHealth } from '../../components'; import type { Pagination } from '../../../../hooks'; -import { useAgentVersion } from '../../../../hooks'; +import { useAgentVersion, useGetListOutputsForPolicies } from '../../../../hooks'; import { useLink, useAuthz } from '../../../../hooks'; import { AgentPolicySummaryLine } from '../../../../components'; import { Tags } from '../../components/tags'; -import type { AgentMetrics } from '../../../../../../../common/types'; +import type { AgentMetrics, OutputsForAgentPolicy } from '../../../../../../../common/types'; import { formatAgentCPU, formatAgentMemory } from '../../services/agent_metrics'; +import { AgentPolicyOutputsSummary } from './agent_policy_outputs_summary'; + import { AgentUpgradeStatus } from './agent_upgrade_status'; import { EmptyPrompt } from './empty_prompt'; -const VERSION_FIELD = 'local_metadata.elastic.agent.version'; -const HOSTNAME_FIELD = 'local_metadata.host.hostname'; +const AGENTS_TABLE_FIELDS = { + ACTIVE: 'active', + HOSTNAME: 'local_metadata.host.hostname', + POLICY: 'policy_id', + METRICS: 'metrics', + VERSION: 'local_metadata.elastic.agent.version', + LAST_CHECKIN: 'last_checkin', + OUTPUT_INTEGRATION: 'output_integrations', + OUTPUT_MONITORING: 'output_monitoring', +}; + function safeMetadata(val: any) { if (typeof val !== 'string') { return '-'; @@ -96,14 +108,33 @@ export const AgentListTable: React.FC = (props: Props) => { const { getHref } = useLink(); const latestAgentVersion = useAgentVersion(); - const isAgentSelectable = (agent: Agent) => { - if (!agent.active) return false; - if (!agent.policy_id) return true; + const isAgentSelectable = useCallback( + (agent: Agent) => { + if (!agent.active) return false; + if (!agent.policy_id) return true; - const agentPolicy = agentPoliciesIndexedById[agent.policy_id]; - const isHosted = agentPolicy?.is_managed === true; - return !isHosted; - }; + const agentPolicy = agentPoliciesIndexedById[agent.policy_id]; + const isHosted = agentPolicy?.is_managed === true; + return !isHosted; + }, + [agentPoliciesIndexedById] + ); + + const agentsShown = useMemo(() => { + return totalAgents + ? showUpgradeable + ? agents.filter((agent) => isAgentSelectable(agent) && isAgentUpgradeable(agent)) + : agents + : []; + }, [agents, isAgentSelectable, showUpgradeable, totalAgents]); + + // get the policyIds of the agents shown on the page + const policyIds = useMemo(() => { + return agentsShown.map((agent) => agent?.policy_id ?? ''); + }, [agentsShown]); + const allOutputs = useGetListOutputsForPolicies({ + ids: policyIds, + }); const noItemsMessage = isLoading && isCurrentRequestIncremented ? ( @@ -140,9 +171,9 @@ export const AgentListTable: React.FC = (props: Props) => { }, }; - const columns = [ + const columns: Array> = [ { - field: 'active', + field: AGENTS_TABLE_FIELDS.ACTIVE, sortable: false, width: '85px', name: i18n.translate('xpack.fleet.agentList.statusColumnTitle', { @@ -151,7 +182,7 @@ export const AgentListTable: React.FC = (props: Props) => { render: (active: boolean, agent: any) => , }, { - field: HOSTNAME_FIELD, + field: AGENTS_TABLE_FIELDS.HOSTNAME, sortable: true, name: i18n.translate('xpack.fleet.agentList.hostColumnTitle', { defaultMessage: 'Host', @@ -171,7 +202,7 @@ export const AgentListTable: React.FC = (props: Props) => { ), }, { - field: 'policy_id', + field: AGENTS_TABLE_FIELDS.POLICY, sortable: true, truncateText: true, name: i18n.translate('xpack.fleet.agentList.policyColumnTitle', { @@ -208,7 +239,7 @@ export const AgentListTable: React.FC = (props: Props) => { ...(displayAgentMetrics ? [ { - field: 'metrics', + field: AGENTS_TABLE_FIELDS.METRICS, sortable: false, name: ( = (props: Props) => { ), }, { - field: 'metrics', + field: AGENTS_TABLE_FIELDS.METRICS, sortable: false, name: ( = (props: Props) => { }, ] : []), - { - field: 'last_checkin', + field: AGENTS_TABLE_FIELDS.LAST_CHECKIN, sortable: true, name: i18n.translate('xpack.fleet.agentList.lastCheckinTitle', { defaultMessage: 'Last activity', }), + width: '100px', + render: (lastCheckin: string) => + lastCheckin ? : undefined, + }, + { + field: AGENTS_TABLE_FIELDS.OUTPUT_INTEGRATION, + sortable: true, + truncateText: true, + name: i18n.translate('xpack.fleet.agentList.integrationsOutputTitle', { + defaultMessage: 'Output for integrations', + }), + width: '180px', + render: (outputs: OutputsForAgentPolicy[], agent: Agent) => { + if (!agent?.policy_id) return null; + + const outputsForPolicy = allOutputs?.data?.items.find( + (item) => item.agentPolicyId === agent?.policy_id + ); + return ; + }, + }, + { + field: AGENTS_TABLE_FIELDS.OUTPUT_MONITORING, + sortable: true, + truncateText: true, + name: i18n.translate('xpack.fleet.agentList.monitoringOutputTitle', { + defaultMessage: 'Output for monitoring', + }), width: '180px', - render: (lastCheckin: string, agent: any) => - lastCheckin ? : null, + render: (outputs: OutputsForAgentPolicy[], agent: Agent) => { + if (!agent?.policy_id) return null; + + const outputsForPolicy = allOutputs?.data?.items.find( + (item) => item.agentPolicyId === agent?.policy_id + ); + return ; + }, }, { - field: VERSION_FIELD, + field: AGENTS_TABLE_FIELDS.VERSION, sortable: true, width: '220px', name: i18n.translate('xpack.fleet.agentList.versionTitle', { @@ -322,13 +386,7 @@ export const AgentListTable: React.FC = (props: Props) => { data-test-subj="fleetAgentListTable" loading={isLoading} noItemsMessage={noItemsMessage} - items={ - totalAgents - ? showUpgradeable - ? agents.filter((agent) => isAgentSelectable(agent) && isAgentUpgradeable(agent)) - : agents - : [] - } + items={agentsShown} itemId="id" columns={columns} pagination={{ diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_policy_outputs_summary.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_policy_outputs_summary.test.tsx new file mode 100644 index 0000000000000..255b2efb94026 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_policy_outputs_summary.test.tsx @@ -0,0 +1,99 @@ +/* + * 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 { act, fireEvent } from '@testing-library/react'; +import React from 'react'; + +import { createFleetTestRendererMock } from '../../../../../../mock'; +import type { TestRenderer } from '../../../../../../mock'; + +import type { OutputsForAgentPolicy } from '../../../../../../../common/types'; + +import { AgentPolicyOutputsSummary } from './agent_policy_outputs_summary'; + +describe('MultipleAgentPolicySummaryLine', () => { + let testRenderer: TestRenderer; + const outputsForPolicy: OutputsForAgentPolicy = { + agentPolicyId: 'policy-1', + monitoring: { + output: { + id: 'elasticsearch1', + name: 'Elasticsearch1', + }, + }, + data: { + output: { + id: 'elasticsearch1', + name: 'Elasticsearch1', + }, + }, + }; + const data = { + data: { + output: { + id: 'elasticsearch1', + name: 'Elasticsearch1', + }, + integrations: [ + { + id: 'remote_es1', + name: 'Remote ES', + pkgName: 'ngnix', + integrationPolicyName: 'Nginx-1', + }, + + { + id: 'logstash', + name: 'Logstash-1', + pkgName: 'apache', + integrationPolicyName: 'Apache-1', + }, + ], + }, + }; + + const render = (outputs?: OutputsForAgentPolicy, isMonitoring?: boolean) => + testRenderer.render( + + ); + + beforeEach(() => { + testRenderer = createFleetTestRendererMock(); + }); + + test('it should render the name associated with the default output when the agent policy does not have custom outputs', async () => { + const results = render(outputsForPolicy); + expect(results.container.textContent).toBe('Elasticsearch1'); + expect(results.queryByTestId('outputNameLink')).toBeInTheDocument(); + expect(results.queryByTestId('outputsIntegrationsNumberBadge')).not.toBeInTheDocument(); + }); + + test('it should render the first output name and the badge when there are multiple outputs associated with integrations', async () => { + const results = render({ ...outputsForPolicy, ...data }); + + expect(results.queryByTestId('outputNameLink')).toBeInTheDocument(); + expect(results.queryByTestId('outputsIntegrationsNumberBadge')).toBeInTheDocument(); + + await act(async () => { + fireEvent.click(results.getByTestId('outputsIntegrationsNumberBadge')); + }); + expect(results.queryByTestId('outputPopover')).toBeInTheDocument(); + expect(results.queryByTestId('output-integration-0')?.textContent).toContain( + 'Nginx-1: Remote ES' + ); + expect(results.queryByTestId('output-integration-1')?.textContent).toContain( + 'Apache-1: Logstash-1' + ); + }); + + test('it should not render the badge when monitoring is true', async () => { + const results = render({ ...outputsForPolicy, ...data }, true); + + expect(results.queryByTestId('outputNameLink')).toBeInTheDocument(); + expect(results.queryByTestId('outputsIntegrationsNumberBadge')).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_policy_outputs_summary.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_policy_outputs_summary.tsx new file mode 100644 index 0000000000000..c0b0e5fbfbccc --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_policy_outputs_summary.tsx @@ -0,0 +1,115 @@ +/* + * 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 styled from 'styled-components'; +import React, { useMemo, useState } from 'react'; + +import type { EuiListGroupItemProps } from '@elastic/eui'; +import { + EuiBadge, + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiListGroup, + EuiPopover, + EuiPopoverTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { useLink } from '../../../../hooks'; +import type { OutputsForAgentPolicy } from '../../../../../../../common/types'; + +const TruncatedEuiLink = styled(EuiLink)` + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: 120px; +`; + +export const AgentPolicyOutputsSummary: React.FC<{ + outputs?: OutputsForAgentPolicy; + isMonitoring?: boolean; +}> = ({ outputs, isMonitoring }) => { + const { getHref } = useLink(); + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const closePopover = () => setIsPopoverOpen(false); + + const monitoring = outputs?.monitoring; + const data = outputs?.data; + + const listItems: EuiListGroupItemProps[] = useMemo(() => { + if (!data?.integrations) return []; + + return (data?.integrations || []).map((integration, index) => { + return { + 'data-test-subj': `output-integration-${index}`, + label: `${integration.integrationPolicyName}: ${integration.name}`, + href: getHref('settings_edit_outputs', { outputId: integration?.id ?? '' }), + iconType: 'dot', + }; + }); + }, [getHref, data?.integrations]); + + return ( + + {isMonitoring ? ( + + + {monitoring?.output.name} + + + ) : ( + + + {data?.output.name} + + + )} + + {data?.integrations && data?.integrations.length >= 1 && !isMonitoring && ( + + setIsPopoverOpen(!isPopoverOpen)} + onClickAriaLabel="Open output integrations popover" + > + +{data?.integrations.length} + + + + {i18n.translate('xpack.fleet.AgentPolicyOutputsSummary.popover.title', { + defaultMessage: 'Output for integrations', + })} + +
+ +
+
+
+ )} +
+ ); +}; diff --git a/x-pack/plugins/fleet/public/hooks/use_request/agent_policy.ts b/x-pack/plugins/fleet/public/hooks/use_request/agent_policy.ts index 9e4fb2344fc29..e130eae49c6eb 100644 --- a/x-pack/plugins/fleet/public/hooks/use_request/agent_policy.ts +++ b/x-pack/plugins/fleet/public/hooks/use_request/agent_policy.ts @@ -23,6 +23,9 @@ import type { DeleteAgentPolicyRequest, DeleteAgentPolicyResponse, BulkGetAgentPoliciesResponse, + GetAgentPolicyOutputsResponse, + GetListAgentPolicyOutputsResponse, + GetListAgentPolicyOutputsRequest, } from '../../types'; import { useRequest, sendRequest, useConditionalRequest, sendRequestForRq } from './use_request'; @@ -201,3 +204,21 @@ export const sendResetAllPreconfiguredAgentPolicies = () => { version: API_VERSIONS.internal.v1, }); }; + +export const useGetListOutputsForPolicies = (body?: GetListAgentPolicyOutputsRequest['body']) => { + return useRequest({ + path: agentPolicyRouteService.getListOutputsPath(), + method: 'post', + body: JSON.stringify(body), + version: API_VERSIONS.public.v1, + }); +}; + +export const useGetInfoOutputsForPolicy = (agentPolicyId: string | undefined) => { + return useConditionalRequest({ + path: agentPolicyId ? agentPolicyRouteService.getInfoOutputsPath(agentPolicyId) : undefined, + method: 'get', + shouldSendRequest: !!agentPolicyId, + version: API_VERSIONS.public.v1, + } as SendConditionalRequestConfig); +}; diff --git a/x-pack/plugins/fleet/public/types/index.ts b/x-pack/plugins/fleet/public/types/index.ts index 099df2ce5a34f..0f0adaba20b5d 100644 --- a/x-pack/plugins/fleet/public/types/index.ts +++ b/x-pack/plugins/fleet/public/types/index.ts @@ -147,6 +147,9 @@ export type { GetEnrollmentSettingsRequest, GetEnrollmentSettingsResponse, GetSpaceSettingsResponse, + GetAgentPolicyOutputsResponse, + GetListAgentPolicyOutputsRequest, + GetListAgentPolicyOutputsResponse, } from '../../common/types'; export { entries, diff --git a/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts b/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts index 2c93880c10609..49b5590a2e761 100644 --- a/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts @@ -33,6 +33,8 @@ import type { BulkGetAgentPoliciesRequestSchema, AgentPolicy, FleetRequestHandlerContext, + GetAgentPolicyOutputsRequestSchema, + GetListAgentPolicyOutputsRequestSchema, } from '../../types'; import type { @@ -47,6 +49,8 @@ import type { GetFullAgentConfigMapResponse, GetFullAgentManifestResponse, BulkGetAgentPoliciesResponse, + GetAgentPolicyOutputsResponse, + GetListAgentPolicyOutputsResponse, } from '../../../common/types'; import { defaultFleetErrorHandler, @@ -678,3 +682,64 @@ export const downloadK8sManifest: FleetRequestHandler< return defaultFleetErrorHandler({ error, response }); } }; + +export const GetAgentPolicyOutputsHandler: FleetRequestHandler< + TypeOf, + undefined +> = async (context, request, response) => { + try { + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const agentPolicy = await agentPolicyService.get(soClient, request.params.agentPolicyId); + + if (!agentPolicy) { + return response.customError({ + statusCode: 404, + body: { message: 'Agent policy not found' }, + }); + } + const outputs = await agentPolicyService.getAllOutputsForPolicy(soClient, agentPolicy); + + const body: GetAgentPolicyOutputsResponse = { + item: outputs, + }; + return response.ok({ + body, + }); + } catch (error) { + return defaultFleetErrorHandler({ error, response }); + } +}; + +export const GetListAgentPolicyOutputsHandler: FleetRequestHandler< + undefined, + undefined, + TypeOf +> = async (context, request, response) => { + try { + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const { ids } = request.body; + + if (!ids) { + return response.ok({ + body: { items: [] }, + }); + } + const agentPolicies = await agentPolicyService.getByIDs(soClient, ids, { + withPackagePolicies: true, + }); + + const outputsList = await agentPolicyService.listAllOutputsForPolicies(soClient, agentPolicies); + + const body: GetListAgentPolicyOutputsResponse = { + items: outputsList, + }; + + return response.ok({ + body, + }); + } catch (error) { + return defaultFleetErrorHandler({ error, response }); + } +}; diff --git a/x-pack/plugins/fleet/server/routes/agent_policy/index.ts b/x-pack/plugins/fleet/server/routes/agent_policy/index.ts index 2ed7079deceec..9311f0ae2acca 100644 --- a/x-pack/plugins/fleet/server/routes/agent_policy/index.ts +++ b/x-pack/plugins/fleet/server/routes/agent_policy/index.ts @@ -28,6 +28,10 @@ import { GetFullAgentPolicyResponseSchema, DownloadFullAgentPolicyResponseSchema, GetK8sManifestResponseScheme, + GetAgentPolicyOutputsRequestSchema, + GetAgentPolicyOutputsResponseSchema, + GetListAgentPolicyOutputsResponseSchema, + GetListAgentPolicyOutputsRequestSchema, } from '../../types'; import { K8S_API_ROUTES } from '../../../common/constants'; @@ -47,6 +51,8 @@ import { downloadK8sManifest, getK8sManifest, bulkGetAgentPoliciesHandler, + GetAgentPolicyOutputsHandler, + GetListAgentPolicyOutputsHandler, } from './handlers'; export const registerRoutes = (router: FleetAuthzRouter) => { @@ -390,4 +396,62 @@ export const registerRoutes = (router: FleetAuthzRouter) => { }, downloadK8sManifest ); + + router.versioned + .post({ + path: AGENT_POLICY_API_ROUTES.LIST_OUTPUTS_PATTERN, + fleetAuthz: (authz) => { + return authz.fleet.readAgentPolicies && authz.fleet.readSettings; + }, + description: `Get list of outputs associated with agent policies`, + options: { + tags: ['oas-tag:Elastic Agent policies'], + }, + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: GetListAgentPolicyOutputsRequestSchema, + response: { + 200: { + body: () => GetListAgentPolicyOutputsResponseSchema, + }, + 400: { + body: genericErrorResponse, + }, + }, + }, + }, + GetListAgentPolicyOutputsHandler + ); + + router.versioned + .get({ + path: AGENT_POLICY_API_ROUTES.INFO_OUTPUTS_PATTERN, + fleetAuthz: (authz) => { + return authz.fleet.readAgentPolicies && authz.fleet.readSettings; + }, + description: `Get list of outputs associated with agent policy by policy id`, + options: { + tags: ['oas-tag:Elastic Agent policies'], + }, + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: GetAgentPolicyOutputsRequestSchema, + response: { + 200: { + body: () => GetAgentPolicyOutputsResponseSchema, + }, + 400: { + body: genericErrorResponse, + }, + }, + }, + }, + GetAgentPolicyOutputsHandler + ); }; diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts index fa5522d50802b..609c560906de2 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts @@ -109,7 +109,7 @@ jest.mock('../output', () => { getDefaultDataOutputId: async () => 'test-id', getDefaultMonitoringOutputId: async () => 'test-id', get: (soClient: any, id: string): Output => OUTPUTS[id] || OUTPUTS['test-id'], - bulkGet: async (soClient: any, ids: string[]): Promise => { + bulkGet: async (ids: string[]): Promise => { return ids.map((id) => OUTPUTS[id] || OUTPUTS['test-id']); }, }, diff --git a/x-pack/plugins/fleet/server/services/agent_policies/related_saved_objects.ts b/x-pack/plugins/fleet/server/services/agent_policies/related_saved_objects.ts index 52dd34a757693..c10edcfeb6edf 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/related_saved_objects.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/related_saved_objects.ts @@ -48,7 +48,7 @@ export async function fetchRelatedSavedObjects( const [outputs, { host: downloadSourceUri, proxy_id: downloadSourceProxyId }, fleetServerHosts] = await Promise.all([ - outputService.bulkGet(soClient, outputIds, { ignoreNotFound: true }), + outputService.bulkGet(outputIds, { ignoreNotFound: true }), getSourceUriForAgentPolicy(soClient, agentPolicy), getFleetServerHostsForAgentPolicy(soClient, agentPolicy).catch((err) => { appContextService diff --git a/x-pack/plugins/fleet/server/services/agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policy.ts index 8ce8c9a4291bc..0209ee6edb630 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { chunk, groupBy, isEqual, keyBy, omit, pick } from 'lodash'; +import { chunk, groupBy, isEqual, keyBy, omit, pick, uniq } from 'lodash'; import { v5 as uuidv5 } from 'uuid'; import { dump } from 'js-yaml'; import pMap from 'p-map'; @@ -61,6 +61,7 @@ import type { PostAgentPolicyCreateCallback, PostAgentPolicyUpdateCallback, PreconfiguredAgentPolicy, + OutputsForAgentPolicy, } from '../types'; import { AGENT_POLICY_INDEX, @@ -74,6 +75,7 @@ import type { FetchAllAgentPoliciesOptions, FetchAllAgentPolicyIdsOptions, FleetServerPolicy, + IntegrationsOutput, PackageInfo, } from '../../common/types'; import { @@ -85,6 +87,7 @@ import { HostedAgentPolicyRestrictionRelatedError, PackagePolicyRestrictionRelatedError, AgentlessPolicyExistsRequestError, + OutputNotFoundError, } from '../errors'; import type { FullAgentConfigMap } from '../../common/types/models/agent_cm'; @@ -1777,6 +1780,95 @@ class AgentPolicyService { }); } + // Get all the outputs per agent policy + public async getAllOutputsForPolicy( + soClient: SavedObjectsClientContract, + agentPolicy: AgentPolicy + ) { + const logger = appContextService.getLogger(); + + const [defaultDataOutputId, defaultMonitoringOutputId] = await Promise.all([ + outputService.getDefaultDataOutputId(soClient), + outputService.getDefaultMonitoringOutputId(soClient), + ]); + + if (!defaultDataOutputId) { + throw new OutputNotFoundError('Default output is not setup'); + } + + const dataOutputId = agentPolicy.data_output_id || defaultDataOutputId; + const monitoringOutputId = + agentPolicy.monitoring_output_id || defaultMonitoringOutputId || dataOutputId; + + const outputIds = uniq([dataOutputId, monitoringOutputId]); + + const fetchedOutputs = await outputService.bulkGet(outputIds, { + ignoreNotFound: true, + }); + + const dataOutput = fetchedOutputs.find((output) => output.id === dataOutputId); + const monitoringOutput = fetchedOutputs.find((output) => output.id === monitoringOutputId); + + let integrationsDataOutputs: IntegrationsOutput[] = []; + if (agentPolicy?.package_policies) { + const integrationsWithOutputs = agentPolicy.package_policies.filter( + (pkgPolicy) => !!pkgPolicy?.output_id + ); + integrationsDataOutputs = await pMap( + integrationsWithOutputs, + async (pkgPolicy) => { + if (pkgPolicy?.output_id) { + try { + const output = await outputService.get(soClient, pkgPolicy.output_id); + return { integrationPolicyName: pkgPolicy?.name, id: output.id, name: output.name }; + } catch (error) { + logger.error( + `error while retrieving output with id "${pkgPolicy.output_id}": ${error}` + ); + } + } + return { integrationPolicyName: pkgPolicy?.name, id: pkgPolicy?.output_id ?? '' }; + }, + { + concurrency: 20, + } + ); + } + const outputs: OutputsForAgentPolicy = { + monitoring: { + output: { + name: monitoringOutput?.name ?? '', + id: monitoringOutput?.id ?? '', + }, + }, + data: { + output: { + name: dataOutput?.name ?? '', + id: dataOutput?.id ?? '', + }, + integrations: integrationsDataOutputs ?? [], + }, + }; + return outputs; + } + + public async listAllOutputsForPolicies( + soClient: SavedObjectsClientContract, + agentPolicies: AgentPolicy[] + ) { + const allOutputs: OutputsForAgentPolicy[] = await pMap( + agentPolicies, + async (agentPolicy) => { + const output = await this.getAllOutputsForPolicy(soClient, agentPolicy); + return { agentPolicyId: agentPolicy.id, ...output }; + }, + { + concurrency: 50, + } + ); + return allOutputs; + } + private checkTamperProtectionLicense(agentPolicy: { is_protected?: boolean }): void { if (agentPolicy?.is_protected && !licenseService.isPlatinum()) { throw new FleetUnauthorizedError('Tamper protection requires Platinum license'); diff --git a/x-pack/plugins/fleet/server/services/output.ts b/x-pack/plugins/fleet/server/services/output.ts index 283e28417998b..b5041d9f1df37 100644 --- a/x-pack/plugins/fleet/server/services/output.ts +++ b/x-pack/plugins/fleet/server/services/output.ts @@ -705,11 +705,7 @@ class OutputService { return outputSavedObjectToOutput(newSo); } - public async bulkGet( - soClient: SavedObjectsClientContract, - ids: string[], - { ignoreNotFound = false } = { ignoreNotFound: true } - ) { + public async bulkGet(ids: string[], { ignoreNotFound = false } = { ignoreNotFound: true }) { const res = await this.encryptedSoClient.bulkGet( ids.map((id) => ({ id: outputIdToUuid(id), type: SAVED_OBJECT_TYPE })) ); diff --git a/x-pack/plugins/fleet/server/services/preconfiguration/outputs.ts b/x-pack/plugins/fleet/server/services/preconfiguration/outputs.ts index 452a73282144e..68b4cf2457e26 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration/outputs.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration/outputs.ts @@ -74,7 +74,6 @@ export async function createOrUpdatePreconfiguredOutputs( } const existingOutputs = await outputService.bulkGet( - soClient, outputs.map(({ id }) => id), { ignoreNotFound: true } ); diff --git a/x-pack/plugins/fleet/server/types/index.tsx b/x-pack/plugins/fleet/server/types/index.tsx index 786db010c8eed..5d118af97f478 100644 --- a/x-pack/plugins/fleet/server/types/index.tsx +++ b/x-pack/plugins/fleet/server/types/index.tsx @@ -101,6 +101,7 @@ export type { InstallLatestExecutedState, TemplateAgentPolicyInput, NewPackagePolicyInput, + OutputsForAgentPolicy, } from '../../common/types'; export { ElasticsearchAssetType, KibanaAssetType, KibanaSavedObjectType } from '../../common/types'; export { dataTypes } from '../../common/constants'; diff --git a/x-pack/plugins/fleet/server/types/models/agent_policy.ts b/x-pack/plugins/fleet/server/types/models/agent_policy.ts index 09c32aeb91af6..e82063e775d70 100644 --- a/x-pack/plugins/fleet/server/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/server/types/models/agent_policy.ts @@ -393,3 +393,35 @@ export const FullAgentPolicyResponseSchema = schema.object({ }) ), }); +const MinimalOutputSchema = schema.object({ + id: schema.string(), + name: schema.string(), +}); + +const IntegrationsOutputSchema = schema.arrayOf( + schema.object({ + pkgName: schema.maybe(schema.string()), + integrationPolicyName: schema.maybe(schema.string()), + id: schema.maybe(schema.string()), + name: schema.maybe(schema.string()), + }) +); + +const OutputsForAgentPolicySchema = schema.object({ + agentPolicyId: schema.maybe(schema.string()), + monitoring: schema.object({ + output: MinimalOutputSchema, + }), + data: schema.object({ + output: MinimalOutputSchema, + integrations: schema.maybe(IntegrationsOutputSchema), + }), +}); + +export const GetAgentPolicyOutputsResponseSchema = schema.object({ + item: OutputsForAgentPolicySchema, +}); + +export const GetListAgentPolicyOutputsResponseSchema = schema.object({ + items: schema.arrayOf(OutputsForAgentPolicySchema), +}); diff --git a/x-pack/plugins/fleet/server/types/rest_spec/agent_policy.ts b/x-pack/plugins/fleet/server/types/rest_spec/agent_policy.ts index b5fc70b70af85..6eb42468a6371 100644 --- a/x-pack/plugins/fleet/server/types/rest_spec/agent_policy.ts +++ b/x-pack/plugins/fleet/server/types/rest_spec/agent_policy.ts @@ -171,3 +171,17 @@ export const GetK8sManifestRequestSchema = { export const GetK8sManifestResponseScheme = schema.object({ item: schema.string(), }); + +export const GetAgentPolicyOutputsRequestSchema = { + params: schema.object({ + agentPolicyId: schema.string(), + }), +}; + +export const GetListAgentPolicyOutputsRequestSchema = { + body: schema.object({ + ids: schema.arrayOf(schema.string(), { + meta: { description: 'list of package policy ids' }, + }), + }), +}; diff --git a/x-pack/test/fleet_api_integration/apis/agent_policy/agent_policy_outputs.ts b/x-pack/test/fleet_api_integration/apis/agent_policy/agent_policy_outputs.ts new file mode 100644 index 0000000000000..74c5af6b0d811 --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/agent_policy/agent_policy_outputs.ts @@ -0,0 +1,282 @@ +/* + * 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 { CreateAgentPolicyResponse } from '@kbn/fleet-plugin/common'; +import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; + +export default function (providerContext: FtrProviderContext) { + const { getService } = providerContext; + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const fleetAndAgents = getService('fleetAndAgents'); + + const createOutput = async ({ + name, + id, + type, + hosts, + }: { + name: string; + id: string; + type: string; + hosts: string[]; + }): Promise => { + const res = await supertest + .post(`/api/fleet/outputs`) + .set('kbn-xsrf', 'xxxx') + .send({ + id, + name, + type, + hosts, + }) + .expect(200); + return res.body.item.id; + }; + + const createAgentPolicy = async ( + name: string, + id: string, + dataOutputId?: string, + monitoringOutputId?: string + ): Promise => { + const res = await supertest + .post(`/api/fleet/agent_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name, + id, + namespace: 'default', + ...(dataOutputId ? { data_output_id: dataOutputId } : {}), + ...(monitoringOutputId ? { monitoring_output_id: monitoringOutputId } : {}), + }) + .expect(200); + return res.body.item; + }; + + const createAgentPolicyWithPackagePolicy = async ({ + name, + id, + outputId, + }: { + name: string; + id: string; + outputId?: string; + }): Promise => { + const { body: res } = await supertest + .post(`/api/fleet/agent_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name, + namespace: 'default', + id, + }) + .expect(200); + + const agentPolicyWithPPId = res.item.id; + // package policy needs to have a custom output_id + await supertest + .post(`/api/fleet/package_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'filetest-1', + description: '', + namespace: 'default', + ...(outputId ? { output_id: outputId } : {}), + policy_id: agentPolicyWithPPId, + inputs: [], + package: { + name: 'filetest', + title: 'For File Tests', + version: '0.1.0', + }, + }) + .expect(200); + return res.item; + }; + + let output1Id = ''; + describe('fleet_agent_policies_outputs', () => { + describe('POST /api/fleet/agent_policies/outputs', () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/fleet/empty_fleet_server'); + await kibanaServer.savedObjects.cleanStandardList(); + await fleetAndAgents.setup(); + + output1Id = await createOutput({ + name: 'Output 1', + id: 'logstash-output-1', + type: 'logstash', + hosts: ['test.fr:443'], + }); + }); + after(async () => { + await supertest + .delete(`/api/fleet/outputs/${output1Id}`) + .set('kbn-xsrf', 'xxxx') + .expect(200); + }); + + it('should get a list of outputs by agent policies', async () => { + await createAgentPolicy('Agent policy with default output', 'agent-policy-1'); + await createAgentPolicy( + 'Agent policy with custom output', + 'agent-policy-2', + output1Id, + output1Id + ); + + const outputsPerPoliciesRes = await supertest + .post(`/api/fleet/agent_policies/outputs`) + .set('kbn-xsrf', 'xxxx') + .send({ + ids: ['agent-policy-1', 'agent-policy-2'], + }) + .expect(200); + expect(outputsPerPoliciesRes.body.items).to.eql([ + { + agentPolicyId: 'agent-policy-1', + monitoring: { + output: { + name: 'default', + id: 'fleet-default-output', + }, + }, + data: { + output: { + name: 'default', + id: 'fleet-default-output', + }, + integrations: [], + }, + }, + { + agentPolicyId: 'agent-policy-2', + monitoring: { + output: { + name: 'Output 1', + id: 'logstash-output-1', + }, + }, + data: { + output: { + name: 'Output 1', + id: 'logstash-output-1', + }, + integrations: [], + }, + }, + ]); + // clean up policies + await supertest + .post(`/api/fleet/agent_policies/delete`) + .send({ agentPolicyId: 'agent-policy-1' }) + .set('kbn-xsrf', 'xxxx') + .expect(200); + await supertest + .post(`/api/fleet/agent_policies/delete`) + .send({ agentPolicyId: 'agent-policy-2' }) + .set('kbn-xsrf', 'xxxx') + .expect(200); + }); + }); + + let output2Id = ''; + describe('GET /api/fleet/agent_policies/{agentPolicyId}/outputs', () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/fleet/empty_fleet_server'); + await kibanaServer.savedObjects.cleanStandardList(); + await fleetAndAgents.setup(); + + output2Id = await createOutput({ + name: 'ES Output 1', + id: 'es-output-1', + type: 'elasticsearch', + hosts: ['https://test.fr:8080'], + }); + }); + after(async () => { + await supertest + .delete(`/api/fleet/outputs/${output2Id}`) + .set('kbn-xsrf', 'xxxx') + .expect(200); + }); + + it('should get the list of outputs related to an agentPolicy id', async () => { + await createAgentPolicy('Agent policy with ES output', 'agent-policy-custom', output2Id); + + const outputsPerPoliciesRes = await supertest + .get(`/api/fleet/agent_policies/agent-policy-custom/outputs`) + .set('kbn-xsrf', 'xxxx') + .expect(200); + expect(outputsPerPoliciesRes.body.item).to.eql({ + monitoring: { + output: { + name: 'default', + id: 'fleet-default-output', + }, + }, + data: { + output: { + name: 'ES Output 1', + id: 'es-output-1', + }, + integrations: [], + }, + }); + + await supertest + .post(`/api/fleet/agent_policies/delete`) + .send({ agentPolicyId: 'agent-policy-custom' }) + .set('kbn-xsrf', 'xxxx') + .expect(200); + }); + + it('should also list the outputs set on integrations if any', async () => { + await createAgentPolicyWithPackagePolicy({ + name: 'Agent Policy with package policy', + id: 'agent-policy-custom-2', + outputId: output2Id, + }); + + const outputsPerPoliciesRes = await supertest + .get(`/api/fleet/agent_policies/agent-policy-custom-2/outputs`) + .set('kbn-xsrf', 'xxxx') + .expect(200); + expect(outputsPerPoliciesRes.body.item).to.eql({ + monitoring: { + output: { + name: 'default', + id: 'fleet-default-output', + }, + }, + data: { + output: { + name: 'default', + id: 'fleet-default-output', + }, + integrations: [ + { + id: 'es-output-1', + integrationPolicyName: 'filetest-1', + name: 'ES Output 1', + }, + ], + }, + }); + + await supertest + .post(`/api/fleet/agent_policies/delete`) + .send({ agentPolicyId: 'agent-policy-custom-2' }) + .set('kbn-xsrf', 'xxxx') + .expect(200); + }); + }); + }); +} diff --git a/x-pack/test/fleet_api_integration/apis/agent_policy/index.js b/x-pack/test/fleet_api_integration/apis/agent_policy/index.js index 9ae58b0089942..b036ab9d8103d 100644 --- a/x-pack/test/fleet_api_integration/apis/agent_policy/index.js +++ b/x-pack/test/fleet_api_integration/apis/agent_policy/index.js @@ -13,5 +13,6 @@ export default function loadTests({ loadTestFile }) { loadTestFile(require.resolve('./privileges')); loadTestFile(require.resolve('./agent_policy_root_integrations')); loadTestFile(require.resolve('./create_standalone_api_key')); + loadTestFile(require.resolve('./agent_policy_outputs')); }); }