diff --git a/x-pack/plugins/fleet/README.md b/x-pack/plugins/fleet/README.md index 29436440fac8b..6c26cb29fc541 100644 --- a/x-pack/plugins/fleet/README.md +++ b/x-pack/plugins/fleet/README.md @@ -129,3 +129,15 @@ $ yarn storybook fleet ``` Write stories by creating `.stories.tsx` files colocated with the components you're working on. Consult the [Storybook docs](https://storybook.js.org/docs/react/get-started/introduction) for more information. + +## Dependent applications using Fleet + +The projects below are dependent on Fleet, most using Fleet API as well. In case of breaking changes in Fleet functionality/API, the project owners have to be notified to make sure they can plan for the necessary changes on their end to avoid unexpected break in functionality. + + * [Elastic Agent](https://github.com/elastic/beats/blob/master/x-pack/elastic-agent): uses Fleet API to enroll agents. [See here](https://github.com/elastic/beats/blob/master/x-pack/elastic-agent/pkg/agent/cmd/container.go) + * [Fleet Server](https://github.com/elastic/fleet-server): uses Fleet API to enroll fleet server [See here](https://github.com/elastic/fleet-server/blob/master/cmd/fleet/router.go) + * [elastic-package](https://github.com/elastic/elastic-package): command line tool, uses Fleet with docker compose and Fleet API [See here](https://github.com/elastic/elastic-package/tree/master/internal/kibana) + * [Azure VM extension](https://github.com/elastic/azure-vm-extension): automation tool for Azure VMs, uses Fleet API to enroll agents [See here](https://github.com/elastic/azure-vm-extension/blob/main/src/handler/windows/scripts/enable.ps1) + * [e2e-testing](https://github.com/elastic/e2e-testing): internal project that runs Fleet and tests Fleet API [See here](https://github.com/elastic/e2e-testing/tree/main/internal/kibana) + * [observability-test-environments](https://github.com/elastic/observability-test-environments): internal project, uses Fleet API [See here](https://github.com/elastic/observability-test-environments/blob/master/ansible/tasks-fleet-config.yml) + * [ECK](https://github.com/elastic/cloud-on-k8s): Elastic Cloud on Kubernetes, orchestrates Elastic Stack applications, including Kibana with Fleet (no direct dependency, has examples that include Fleet config) [See here](https://github.com/elastic/cloud-on-k8s/blob/main/docs/orchestrating-elastic-stack-applications/agent-fleet.asciidoc) \ No newline at end of file diff --git a/x-pack/plugins/fleet/common/constants/epm.ts b/x-pack/plugins/fleet/common/constants/epm.ts index 97672f4d4d657..12885be5a8aea 100644 --- a/x-pack/plugins/fleet/common/constants/epm.ts +++ b/x-pack/plugins/fleet/common/constants/epm.ts @@ -23,16 +23,9 @@ export const STANDALONE_RUN_INSTRUCTIONS_WINDOWS = '.\\elastic-agent.exe install /* Package rules: -| | unremovablePackages | defaultPackages | autoUpdatePackages | -|---------------|:---------------------:|:---------------:|:------------------:| -| Removable | ❌ | ✔️ | ✔️ | -| Auto-installs | ❌ | ✔️ | ❌ | -| Auto-updates | ❌ | ✔️ | ✔️ | - -`endpoint` is a special package. It needs to autoupdate, it needs to _not_ be -removable, but it doesn't install by default. Following the table, it needs to -be in `unremovablePackages` and in `autoUpdatePackages`, but not in -`defaultPackages`. +| | autoUpdatePackages | +|---------------|:------------------:| +| Auto-updates | ✔️ | We also define "auto upgrade policies" packages below. These are packages that are considered "stack-aligned" @@ -42,15 +35,6 @@ in their custom policy editor implementations. */ -export const unremovablePackages = [ - FLEET_SYSTEM_PACKAGE, - FLEET_ELASTIC_AGENT_PACKAGE, - FLEET_SERVER_PACKAGE, - FLEET_ENDPOINT_PACKAGE, -]; - -export const defaultPackages = unremovablePackages.filter((p) => p !== FLEET_ENDPOINT_PACKAGE); - export const autoUpdatePackages = [ FLEET_ENDPOINT_PACKAGE, FLEET_APM_PACKAGE, diff --git a/x-pack/plugins/fleet/common/constants/preconfiguration.ts b/x-pack/plugins/fleet/common/constants/preconfiguration.ts index 7d22716bc0f1e..5689223852a32 100644 --- a/x-pack/plugins/fleet/common/constants/preconfiguration.ts +++ b/x-pack/plugins/fleet/common/constants/preconfiguration.ts @@ -6,18 +6,8 @@ */ import { uniqBy } from 'lodash'; -import uuidv5 from 'uuid/v5'; -import type { PreconfiguredAgentPolicy } from '../types'; - -import { - defaultPackages, - FLEET_SYSTEM_PACKAGE, - FLEET_SERVER_PACKAGE, - autoUpdatePackages, - monitoringTypes, - autoUpgradePoliciesPackages, -} from './epm'; +import { autoUpdatePackages, autoUpgradePoliciesPackages } from './epm'; // UUID v5 values require a namespace. We use UUID v5 for some of our preconfigured ID values. export const UUID_V5_NAMESPACE = 'dde7c2de-1370-4c19-9975-b473d0e03508'; @@ -27,63 +17,6 @@ export const PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE = export const PRECONFIGURATION_LATEST_KEYWORD = 'latest'; -type PreconfiguredAgentPolicyWithDefaultInputs = Omit< - PreconfiguredAgentPolicy, - 'package_policies' -> & { - package_policies: Array>; -}; - -export const DEFAULT_AGENT_POLICY_ID_SEED = 'default-agent-policy'; -export const DEFAULT_SYSTEM_PACKAGE_POLICY_ID = 'default-system-policy'; - -export const DEFAULT_AGENT_POLICY: PreconfiguredAgentPolicyWithDefaultInputs = { - id: uuidv5(DEFAULT_AGENT_POLICY_ID_SEED, UUID_V5_NAMESPACE), - name: 'Default policy', - namespace: 'default', - description: 'Default agent policy created by Kibana', - package_policies: [ - { - id: DEFAULT_SYSTEM_PACKAGE_POLICY_ID, - name: `${FLEET_SYSTEM_PACKAGE}-1`, - package: { - name: FLEET_SYSTEM_PACKAGE, - }, - }, - ], - is_default: true, - is_managed: false, - monitoring_enabled: monitoringTypes, -}; - -export const DEFAULT_FLEET_SERVER_POLICY_ID = 'default-fleet-server-agent-policy'; -export const DEFAULT_FLEET_SERVER_AGENT_POLICY_ID_SEED = 'default-fleet-server'; - -export const DEFAULT_FLEET_SERVER_AGENT_POLICY: PreconfiguredAgentPolicyWithDefaultInputs = { - id: uuidv5(DEFAULT_FLEET_SERVER_AGENT_POLICY_ID_SEED, UUID_V5_NAMESPACE), - name: 'Default Fleet Server policy', - namespace: 'default', - description: 'Default Fleet Server agent policy created by Kibana', - package_policies: [ - { - id: DEFAULT_FLEET_SERVER_POLICY_ID, - name: `${FLEET_SERVER_PACKAGE}-1`, - package: { - name: FLEET_SERVER_PACKAGE, - }, - }, - ], - is_default: false, - is_default_fleet_server: true, - is_managed: false, - monitoring_enabled: monitoringTypes, -}; - -export const DEFAULT_PACKAGES = defaultPackages.map((name) => ({ - name, - version: PRECONFIGURATION_LATEST_KEYWORD, -})); - export const AUTO_UPDATE_PACKAGES = autoUpdatePackages.map((name) => ({ name, version: PRECONFIGURATION_LATEST_KEYWORD, @@ -97,7 +30,7 @@ export const AUTO_UPGRADE_POLICIES_PACKAGES = autoUpgradePoliciesPackages.map((n // Controls whether the `Keep Policies up to date` setting is exposed to the user export const KEEP_POLICIES_UP_TO_DATE_PACKAGES = uniqBy( - [...AUTO_UPGRADE_POLICIES_PACKAGES, ...DEFAULT_PACKAGES, ...AUTO_UPDATE_PACKAGES], + [...AUTO_UPGRADE_POLICIES_PACKAGES, ...AUTO_UPDATE_PACKAGES], ({ name }) => name ); diff --git a/x-pack/plugins/fleet/common/openapi/bundled.json b/x-pack/plugins/fleet/common/openapi/bundled.json index 7a2b8af907e5b..98824ccdbfbcf 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.json +++ b/x-pack/plugins/fleet/common/openapi/bundled.json @@ -3002,9 +3002,14 @@ ] }, "is_default": { - "type": "boolean" + "type": "boolean", + "deprecated": true }, "is_default_fleet_server": { + "type": "boolean", + "deprecated": true + }, + "has_fleet_server": { "type": "boolean" }, "data_output_id": { diff --git a/x-pack/plugins/fleet/common/openapi/bundled.yaml b/x-pack/plugins/fleet/common/openapi/bundled.yaml index bee0294c9f3b9..6af428f3c3522 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.yaml +++ b/x-pack/plugins/fleet/common/openapi/bundled.yaml @@ -1850,8 +1850,12 @@ components: - type: number is_default: type: boolean + deprecated: true is_default_fleet_server: type: boolean + deprecated: true + has_fleet_server: + type: boolean data_output_id: type: string monitoring_output_id: diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/preconfigured_agent_policies.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/preconfigured_agent_policies.yaml index 3e3cead7dadca..84feaf8fdeb91 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/preconfigured_agent_policies.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/preconfigured_agent_policies.yaml @@ -24,8 +24,12 @@ properties: - type: number is_default: type: boolean + deprecated: true is_default_fleet_server: type: boolean + deprecated: true + has_fleet_server: + type: boolean data_output_id: type: string monitoring_output_id: 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 3f9e43e72c51d..6e59cf0d50006 100644 --- a/x-pack/plugins/fleet/common/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/common/types/models/agent_policy.ts @@ -19,6 +19,7 @@ export interface NewAgentPolicy { description?: string; is_default?: boolean; is_default_fleet_server?: boolean; // Optional when creating a policy + has_fleet_server?: boolean; is_managed?: boolean; // Optional when creating a policy monitoring_enabled?: MonitoringType; unenroll_timeout?: number; diff --git a/x-pack/plugins/fleet/cypress/fixtures/integrations/agent_policies.json b/x-pack/plugins/fleet/cypress/fixtures/integrations/agent_policies.json deleted file mode 100644 index ba1360e11a21d..0000000000000 --- a/x-pack/plugins/fleet/cypress/fixtures/integrations/agent_policies.json +++ /dev/null @@ -1,978 +0,0 @@ -{ - "items": [ - { - "id": "30e16140-2106-11ec-a289-25321523992d", - "namespace": "default", - "monitoring_enabled": [ - "logs", - "metrics" - ], - "name": "Default policy", - "description": "Default agent policy created by Kibana", - "is_default": true, - "is_preconfigured": true, - "status": "active", - "is_managed": false, - "revision": 4, - "updated_at": "2021-09-29T09:52:13.879Z", - "updated_by": "elastic", - "package_policies": [ - { - "id": "15785537-fdf2-4e38-bd49-ae0537bbe162", - "version": "WzU5NSwxXQ==", - "name": "system-1", - "namespace": "default", - "package": { - "name": "system", - "title": "System", - "version": "1.4.0" - }, - "enabled": true, - "policy_id": "30e16140-2106-11ec-a289-25321523992d", - "output_id": "1ffdf460-2106-11ec-a289-25321523992d", - "inputs": [ - { - "type": "logfile", - "policy_template": "system", - "enabled": true, - "streams": [ - { - "enabled": true, - "data_stream": { - "type": "logs", - "dataset": "system.auth" - }, - "vars": { - "paths": { - "value": [ - "/var/log/auth.log*", - "/var/log/secure*" - ], - "type": "text" - } - }, - "id": "logfile-system.auth-15785537-fdf2-4e38-bd49-ae0537bbe162", - "compiled_stream": { - "paths": [ - "/var/log/auth.log*", - "/var/log/secure*" - ], - "exclude_files": [ - ".gz$" - ], - "multiline": { - "pattern": "^\\s", - "match": "after" - }, - "processors": [ - { - "add_locale": null - } - ] - } - }, - { - "enabled": true, - "data_stream": { - "type": "logs", - "dataset": "system.syslog" - }, - "vars": { - "paths": { - "value": [ - "/var/log/messages*", - "/var/log/syslog*" - ], - "type": "text" - } - }, - "id": "logfile-system.syslog-15785537-fdf2-4e38-bd49-ae0537bbe162", - "compiled_stream": { - "paths": [ - "/var/log/messages*", - "/var/log/syslog*" - ], - "exclude_files": [ - ".gz$" - ], - "multiline": { - "pattern": "^\\s", - "match": "after" - }, - "processors": [ - { - "add_locale": null - } - ] - } - } - ] - }, - { - "type": "winlog", - "policy_template": "system", - "enabled": true, - "streams": [ - { - "enabled": true, - "data_stream": { - "type": "logs", - "dataset": "system.application" - }, - "vars": { - "event_id": { - "type": "text" - }, - "processors": { - "type": "yaml" - }, - "tags": { - "value": [], - "type": "text" - } - }, - "id": "winlog-system.application-15785537-fdf2-4e38-bd49-ae0537bbe162", - "compiled_stream": { - "name": "Application", - "condition": "${host.platform} == 'windows'", - "ignore_older": "72h", - "tags": null - } - }, - { - "enabled": true, - "data_stream": { - "type": "logs", - "dataset": "system.security" - }, - "vars": { - "event_id": { - "type": "text" - }, - "processors": { - "type": "yaml" - }, - "tags": { - "value": [], - "type": "text" - } - }, - "id": "winlog-system.security-15785537-fdf2-4e38-bd49-ae0537bbe162", - "compiled_stream": { - "name": "Security", - "condition": "${host.platform} == 'windows'", - "tags": null - } - }, - { - "enabled": true, - "data_stream": { - "type": "logs", - "dataset": "system.system" - }, - "vars": { - "event_id": { - "type": "text" - }, - "processors": { - "type": "yaml" - }, - "tags": { - "value": [], - "type": "text" - } - }, - "id": "winlog-system.system-15785537-fdf2-4e38-bd49-ae0537bbe162", - "compiled_stream": { - "name": "System", - "condition": "${host.platform} == 'windows'", - "tags": null - } - } - ], - "vars": { - "preserve_original_event": { - "value": false, - "type": "bool" - } - } - }, - { - "type": "system/metrics", - "policy_template": "system", - "enabled": true, - "streams": [ - { - "enabled": false, - "data_stream": { - "type": "metrics", - "dataset": "system.core" - }, - "vars": { - "period": { - "value": "10s", - "type": "text" - }, - "core.metrics": { - "value": [ - "percentages" - ], - "type": "text" - } - }, - "id": "system/metrics-system.core-15785537-fdf2-4e38-bd49-ae0537bbe162" - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.cpu" - }, - "vars": { - "period": { - "value": "10s", - "type": "text" - }, - "cpu.metrics": { - "value": [ - "percentages", - "normalized_percentages" - ], - "type": "text" - } - }, - "id": "system/metrics-system.cpu-15785537-fdf2-4e38-bd49-ae0537bbe162", - "compiled_stream": { - "metricsets": [ - "cpu" - ], - "cpu.metrics": [ - "percentages", - "normalized_percentages" - ], - "period": "10s" - } - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.diskio" - }, - "vars": { - "period": { - "value": "10s", - "type": "text" - }, - "diskio.include_devices": { - "value": [], - "type": "text" - } - }, - "id": "system/metrics-system.diskio-15785537-fdf2-4e38-bd49-ae0537bbe162", - "compiled_stream": { - "metricsets": [ - "diskio" - ], - "diskio.include_devices": null, - "period": "10s" - } - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.filesystem" - }, - "vars": { - "period": { - "value": "1m", - "type": "text" - }, - "processors": { - "value": "- drop_event.when.regexp:\n system.filesystem.mount_point: ^/(sys|cgroup|proc|dev|etc|host|lib|snap)($|/)\n", - "type": "yaml" - } - }, - "id": "system/metrics-system.filesystem-15785537-fdf2-4e38-bd49-ae0537bbe162", - "compiled_stream": { - "metricsets": [ - "filesystem" - ], - "period": "1m", - "processors": [ - { - "drop_event.when.regexp": { - "system.filesystem.mount_point": "^/(sys|cgroup|proc|dev|etc|host|lib|snap)($|/)" - } - } - ] - } - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.fsstat" - }, - "vars": { - "period": { - "value": "1m", - "type": "text" - }, - "processors": { - "value": "- drop_event.when.regexp:\n system.fsstat.mount_point: ^/(sys|cgroup|proc|dev|etc|host|lib|snap)($|/)\n", - "type": "yaml" - } - }, - "id": "system/metrics-system.fsstat-15785537-fdf2-4e38-bd49-ae0537bbe162", - "compiled_stream": { - "metricsets": [ - "fsstat" - ], - "period": "1m", - "processors": [ - { - "drop_event.when.regexp": { - "system.fsstat.mount_point": "^/(sys|cgroup|proc|dev|etc|host|lib|snap)($|/)" - } - } - ] - } - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.load" - }, - "vars": { - "period": { - "value": "10s", - "type": "text" - } - }, - "id": "system/metrics-system.load-15785537-fdf2-4e38-bd49-ae0537bbe162", - "compiled_stream": { - "metricsets": [ - "load" - ], - "condition": "${host.platform} != 'windows'", - "period": "10s" - } - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.memory" - }, - "vars": { - "period": { - "value": "10s", - "type": "text" - } - }, - "id": "system/metrics-system.memory-15785537-fdf2-4e38-bd49-ae0537bbe162", - "compiled_stream": { - "metricsets": [ - "memory" - ], - "period": "10s" - } - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.network" - }, - "vars": { - "period": { - "value": "10s", - "type": "text" - }, - "network.interfaces": { - "value": [], - "type": "text" - } - }, - "id": "system/metrics-system.network-15785537-fdf2-4e38-bd49-ae0537bbe162", - "compiled_stream": { - "metricsets": [ - "network" - ], - "period": "10s", - "network.interfaces": null - } - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.process" - }, - "vars": { - "period": { - "value": "10s", - "type": "text" - }, - "process.include_top_n.by_cpu": { - "value": 5, - "type": "integer" - }, - "process.include_top_n.by_memory": { - "value": 5, - "type": "integer" - }, - "process.cmdline.cache.enabled": { - "value": true, - "type": "bool" - }, - "process.cgroups.enabled": { - "value": false, - "type": "bool" - }, - "process.env.whitelist": { - "value": [], - "type": "text" - }, - "process.include_cpu_ticks": { - "value": false, - "type": "bool" - }, - "processes": { - "value": [ - ".*" - ], - "type": "text" - } - }, - "id": "system/metrics-system.process-15785537-fdf2-4e38-bd49-ae0537bbe162", - "compiled_stream": { - "metricsets": [ - "process" - ], - "period": "10s", - "process.include_top_n.by_cpu": 5, - "process.include_top_n.by_memory": 5, - "process.cmdline.cache.enabled": true, - "process.cgroups.enabled": false, - "process.include_cpu_ticks": false, - "processes": [ - ".*" - ] - } - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.process.summary" - }, - "vars": { - "period": { - "value": "10s", - "type": "text" - } - }, - "id": "system/metrics-system.process.summary-15785537-fdf2-4e38-bd49-ae0537bbe162", - "compiled_stream": { - "metricsets": [ - "process_summary" - ], - "period": "10s" - } - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.socket_summary" - }, - "vars": { - "period": { - "value": "10s", - "type": "text" - } - }, - "id": "system/metrics-system.socket_summary-15785537-fdf2-4e38-bd49-ae0537bbe162", - "compiled_stream": { - "metricsets": [ - "socket_summary" - ], - "period": "10s" - } - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.uptime" - }, - "vars": { - "period": { - "value": "10s", - "type": "text" - } - }, - "id": "system/metrics-system.uptime-15785537-fdf2-4e38-bd49-ae0537bbe162", - "compiled_stream": { - "metricsets": [ - "uptime" - ], - "period": "10s" - } - } - ], - "vars": { - "system.hostfs": { - "type": "text" - } - } - }, - { - "type": "httpjson", - "policy_template": "system", - "enabled": false, - "streams": [ - { - "enabled": false, - "data_stream": { - "type": "logs", - "dataset": "system.application" - }, - "vars": { - "interval": { - "value": "10s", - "type": "text" - }, - "search": { - "value": "search sourcetype=\"XmlWinEventLog:Application\"", - "type": "text" - }, - "tags": { - "value": [ - "forwarded" - ], - "type": "text" - } - }, - "id": "httpjson-system.application-15785537-fdf2-4e38-bd49-ae0537bbe162" - }, - { - "enabled": false, - "data_stream": { - "type": "logs", - "dataset": "system.security" - }, - "vars": { - "interval": { - "value": "10s", - "type": "text" - }, - "search": { - "value": "search sourcetype=\"XmlWinEventLog:Security\"", - "type": "text" - }, - "tags": { - "value": [ - "forwarded" - ], - "type": "text" - } - }, - "id": "httpjson-system.security-15785537-fdf2-4e38-bd49-ae0537bbe162" - }, - { - "enabled": false, - "data_stream": { - "type": "logs", - "dataset": "system.system" - }, - "vars": { - "interval": { - "value": "10s", - "type": "text" - }, - "search": { - "value": "search sourcetype=\"XmlWinEventLog:System\"", - "type": "text" - }, - "tags": { - "value": [ - "forwarded" - ], - "type": "text" - } - }, - "id": "httpjson-system.system-15785537-fdf2-4e38-bd49-ae0537bbe162" - } - ], - "vars": { - "url": { - "value": "https://server.example.com:8089", - "type": "text" - }, - "username": { - "type": "text" - }, - "password": { - "type": "password" - }, - "token": { - "type": "password" - }, - "preserve_original_event": { - "value": false, - "type": "bool" - }, - "ssl": { - "value": "#certificate_authorities:\n# - |\n# -----BEGIN CERTIFICATE-----\n# MIIDCjCCAfKgAwIBAgITJ706Mu2wJlKckpIvkWxEHvEyijANBgkqhkiG9w0BAQsF\n# ADAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwIBcNMTkwNzIyMTkyOTA0WhgPMjExOTA2\n# MjgxOTI5MDRaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEB\n# BQADggEPADCCAQoCggEBANce58Y/JykI58iyOXpxGfw0/gMvF0hUQAcUrSMxEO6n\n# fZRA49b4OV4SwWmA3395uL2eB2NB8y8qdQ9muXUdPBWE4l9rMZ6gmfu90N5B5uEl\n# 94NcfBfYOKi1fJQ9i7WKhTjlRkMCgBkWPkUokvBZFRt8RtF7zI77BSEorHGQCk9t\n# /D7BS0GJyfVEhftbWcFEAG3VRcoMhF7kUzYwp+qESoriFRYLeDWv68ZOvG7eoWnP\n# PsvZStEVEimjvK5NSESEQa9xWyJOmlOKXhkdymtcUd/nXnx6UTCFgnkgzSdTWV41\n# CI6B6aJ9svCTI2QuoIq2HxX/ix7OvW1huVmcyHVxyUECAwEAAaNTMFEwHQYDVR0O\n# BBYEFPwN1OceFGm9v6ux8G+DZ3TUDYxqMB8GA1UdIwQYMBaAFPwN1OceFGm9v6ux\n# 8G+DZ3TUDYxqMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG5D\n# 874A4YI7YUwOVsVAdbWtgp1d0zKcPRR+r2OdSbTAV5/gcS3jgBJ3i1BN34JuDVFw\n# 3DeJSYT3nxy2Y56lLnxDeF8CUTUtVQx3CuGkRg1ouGAHpO/6OqOhwLLorEmxi7tA\n# H2O8mtT0poX5AnOAhzVy7QW0D/k4WaoLyckM5hUa6RtvgvLxOwA0U+VGurCDoctu\n# 8F4QOgTAWyh8EZIwaKCliFRSynDpv3JTUwtfZkxo6K6nce1RhCWFAsMvDZL8Dgc0\n# yvgJ38BRsFOtkRuAGSf6ZUwTO8JJRRIFnpUzXflAnGivK9M13D5GEQMmIl6U9Pvk\n# sxSmbIUfc2SGJGCJD4I=\n# -----END CERTIFICATE-----\n", - "type": "yaml" - } - } - } - ], - "revision": 1, - "created_at": "2021-09-29T09:18:23.207Z", - "created_by": "system", - "updated_at": "2021-09-29T09:18:23.207Z", - "updated_by": "system" - }, - { - "id": "63172a6b-4f00-4376-b5e6-fe9b3f00fc79", - "version": "WzczOSwxXQ==", - "name": "apache-1", - "description": "", - "namespace": "default", - "policy_id": "30e16140-2106-11ec-a289-25321523992d", - "enabled": true, - "output_id": "", - "inputs": [ - { - "type": "logfile", - "policy_template": "apache", - "enabled": true, - "streams": [ - { - "enabled": true, - "data_stream": { - "type": "logs", - "dataset": "apache.access" - }, - "vars": { - "paths": { - "value": [ - "/var/log/apache2/access.log*", - "/var/log/apache2/other_vhosts_access.log*", - "/var/log/httpd/access_log*" - ], - "type": "text" - }, - "tags": { - "value": [ - "apache-access" - ], - "type": "text" - }, - "preserve_original_event": { - "value": false, - "type": "bool" - }, - "processors": { - "type": "yaml" - } - }, - "id": "logfile-apache.access-63172a6b-4f00-4376-b5e6-fe9b3f00fc79", - "compiled_stream": { - "paths": [ - "/var/log/apache2/access.log*", - "/var/log/apache2/other_vhosts_access.log*", - "/var/log/httpd/access_log*" - ], - "tags": [ - "apache-access" - ], - "exclude_files": [ - ".gz$" - ] - } - }, - { - "enabled": true, - "data_stream": { - "type": "logs", - "dataset": "apache.error" - }, - "vars": { - "paths": { - "value": [ - "/var/log/apache2/error.log*", - "/var/log/httpd/error_log*" - ], - "type": "text" - }, - "tags": { - "value": [ - "apache-error" - ], - "type": "text" - }, - "preserve_original_event": { - "value": false, - "type": "bool" - }, - "processors": { - "type": "yaml" - } - }, - "id": "logfile-apache.error-63172a6b-4f00-4376-b5e6-fe9b3f00fc79", - "compiled_stream": { - "paths": [ - "/var/log/apache2/error.log*", - "/var/log/httpd/error_log*" - ], - "exclude_files": [ - ".gz$" - ], - "tags": [ - "apache-error" - ], - "processors": [ - { - "add_locale": null - } - ] - } - } - ] - }, - { - "type": "httpjson", - "policy_template": "apache", - "enabled": false, - "streams": [ - { - "enabled": false, - "data_stream": { - "type": "logs", - "dataset": "apache.access" - }, - "vars": { - "interval": { - "value": "10s", - "type": "text" - }, - "search": { - "value": "search sourcetype=\"access*\"", - "type": "text" - }, - "tags": { - "value": [ - "forwarded", - "apache-access" - ], - "type": "text" - }, - "preserve_original_event": { - "value": false, - "type": "bool" - }, - "processors": { - "type": "yaml" - } - }, - "id": "httpjson-apache.access-63172a6b-4f00-4376-b5e6-fe9b3f00fc79" - }, - { - "enabled": false, - "data_stream": { - "type": "logs", - "dataset": "apache.error" - }, - "vars": { - "interval": { - "value": "10s", - "type": "text" - }, - "search": { - "value": "search sourcetype=apache:error OR sourcetype=apache_error", - "type": "text" - }, - "tags": { - "value": [ - "forwarded", - "apache-error" - ], - "type": "text" - }, - "preserve_original_event": { - "value": false, - "type": "bool" - }, - "processors": { - "type": "yaml" - } - }, - "id": "httpjson-apache.error-63172a6b-4f00-4376-b5e6-fe9b3f00fc79" - } - ], - "vars": { - "url": { - "value": "https://server.example.com:8089", - "type": "text" - }, - "username": { - "type": "text" - }, - "password": { - "type": "password" - }, - "token": { - "type": "password" - }, - "ssl": { - "value": "#certificate_authorities:\n# - |\n# -----BEGIN CERTIFICATE-----\n# MIIDCjCCAfKgAwIBAgITJ706Mu2wJlKckpIvkWxEHvEyijANBgkqhkiG9w0BAQsF\n# ADAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwIBcNMTkwNzIyMTkyOTA0WhgPMjExOTA2\n# MjgxOTI5MDRaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEB\n# BQADggEPADCCAQoCggEBANce58Y/JykI58iyOXpxGfw0/gMvF0hUQAcUrSMxEO6n\n# fZRA49b4OV4SwWmA3395uL2eB2NB8y8qdQ9muXUdPBWE4l9rMZ6gmfu90N5B5uEl\n# 94NcfBfYOKi1fJQ9i7WKhTjlRkMCgBkWPkUokvBZFRt8RtF7zI77BSEorHGQCk9t\n# /D7BS0GJyfVEhftbWcFEAG3VRcoMhF7kUzYwp+qESoriFRYLeDWv68ZOvG7eoWnP\n# PsvZStEVEimjvK5NSESEQa9xWyJOmlOKXhkdymtcUd/nXnx6UTCFgnkgzSdTWV41\n# CI6B6aJ9svCTI2QuoIq2HxX/ix7OvW1huVmcyHVxyUECAwEAAaNTMFEwHQYDVR0O\n# BBYEFPwN1OceFGm9v6ux8G+DZ3TUDYxqMB8GA1UdIwQYMBaAFPwN1OceFGm9v6ux\n# 8G+DZ3TUDYxqMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG5D\n# 874A4YI7YUwOVsVAdbWtgp1d0zKcPRR+r2OdSbTAV5/gcS3jgBJ3i1BN34JuDVFw\n# 3DeJSYT3nxy2Y56lLnxDeF8CUTUtVQx3CuGkRg1ouGAHpO/6OqOhwLLorEmxi7tA\n# H2O8mtT0poX5AnOAhzVy7QW0D/k4WaoLyckM5hUa6RtvgvLxOwA0U+VGurCDoctu\n# 8F4QOgTAWyh8EZIwaKCliFRSynDpv3JTUwtfZkxo6K6nce1RhCWFAsMvDZL8Dgc0\n# yvgJ38BRsFOtkRuAGSf6ZUwTO8JJRRIFnpUzXflAnGivK9M13D5GEQMmIl6U9Pvk\n# sxSmbIUfc2SGJGCJD4I=\n# -----END CERTIFICATE-----\n", - "type": "yaml" - } - } - }, - { - "type": "apache/metrics", - "policy_template": "apache", - "enabled": true, - "streams": [ - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "apache.status" - }, - "vars": { - "period": { - "value": "30s", - "type": "text" - }, - "server_status_path": { - "value": "/server-status", - "type": "text" - } - }, - "id": "apache/metrics-apache.status-63172a6b-4f00-4376-b5e6-fe9b3f00fc79", - "compiled_stream": { - "metricsets": [ - "status" - ], - "hosts": [ - "http://127.0.0.1" - ], - "period": "30s", - "server_status_path": "/server-status" - } - } - ], - "vars": { - "hosts": { - "value": [ - "http://127.0.0.1" - ], - "type": "text" - } - } - } - ], - "package": { - "name": "apache", - "title": "Apache", - "version": "1.1.0" - }, - "revision": 1, - "created_at": "2021-09-29T09:52:12.865Z", - "created_by": "elastic", - "updated_at": "2021-09-29T09:52:12.865Z", - "updated_by": "elastic" - } - ], - "agents": 1 - }, - { - "id": "30e16141-2106-11ec-a289-25321523992d", - "namespace": "default", - "monitoring_enabled": [ - "logs", - "metrics" - ], - "name": "Default Fleet Server policy", - "description": "Default Fleet Server agent policy created by Kibana", - "is_default": false, - "is_default_fleet_server": true, - "is_preconfigured": true, - "status": "active", - "is_managed": false, - "revision": 1, - "updated_at": "2021-09-29T09:18:25.581Z", - "updated_by": "system", - "package_policies": [ - { - "id": "3f79c8a2-ed32-45d9-a7e7-b58852f4cb7d", - "version": "WzU5NywxXQ==", - "name": "fleet_server-1", - "namespace": "default", - "package": { - "name": "fleet_server", - "title": "Fleet Server", - "version": "1.0.1" - }, - "enabled": true, - "policy_id": "30e16141-2106-11ec-a289-25321523992d", - "output_id": "1ffdf460-2106-11ec-a289-25321523992d", - "inputs": [ - { - "type": "fleet-server", - "policy_template": "fleet_server", - "enabled": true, - "streams": [], - "vars": { - "host": { - "value": [ - "0.0.0.0" - ], - "type": "text" - }, - "port": { - "value": [ - 8220 - ], - "type": "integer" - }, - "max_connections": { - "type": "integer" - }, - "custom": { - "value": "", - "type": "yaml" - } - }, - "compiled_input": { - "server": { - "port": 8220, - "host": "0.0.0.0" - } - } - } - ], - "revision": 1, - "created_at": "2021-09-29T09:18:25.204Z", - "created_by": "system", - "updated_at": "2021-09-29T09:18:25.204Z", - "updated_by": "system" - } - ], - "agents": 0 - } - ], - "total": 2, - "page": 1, - "perPage": 20 -} \ No newline at end of file diff --git a/x-pack/plugins/fleet/cypress/fixtures/integrations/agent_policy.json b/x-pack/plugins/fleet/cypress/fixtures/integrations/agent_policy.json deleted file mode 100644 index aa6520f513acd..0000000000000 --- a/x-pack/plugins/fleet/cypress/fixtures/integrations/agent_policy.json +++ /dev/null @@ -1,644 +0,0 @@ -{ - "item": { - "id": "30e16140-2106-11ec-a289-25321523992d", - "namespace": "default", - "monitoring_enabled": [ - "logs", - "metrics" - ], - "name": "Default policy", - "description": "Default agent policy created by Kibana", - "is_default": true, - "is_preconfigured": true, - "status": "active", - "is_managed": false, - "revision": 1, - "updated_at": "2021-09-30T10:02:50.389Z", - "updated_by": "system", - "package_policies": [ - { - "id": "4243f6b9-6ce2-48ec-859a-b5df4baa7c11", - "version": "WzEyNjQsMV0=", - "name": "system-1", - "namespace": "default", - "package": { - "name": "system", - "title": "System", - "version": "1.4.0" - }, - "enabled": true, - "policy_id": "8f108d20-21d5-11ec-9dad-073c0cd6096b", - "output_id": "4f979e90-21d5-11ec-9dad-073c0cd6096b", - "inputs": [ - { - "type": "logfile", - "policy_template": "system", - "enabled": true, - "streams": [ - { - "enabled": true, - "data_stream": { - "type": "logs", - "dataset": "system.auth" - }, - "vars": { - "paths": { - "value": [ - "/var/log/auth.log*", - "/var/log/secure*" - ], - "type": "text" - } - }, - "id": "logfile-system.auth-4243f6b9-6ce2-48ec-859a-b5df4baa7c11", - "compiled_stream": { - "paths": [ - "/var/log/auth.log*", - "/var/log/secure*" - ], - "exclude_files": [ - ".gz$" - ], - "multiline": { - "pattern": "^\\s", - "match": "after" - }, - "processors": [ - { - "add_locale": null - } - ] - } - }, - { - "enabled": true, - "data_stream": { - "type": "logs", - "dataset": "system.syslog" - }, - "vars": { - "paths": { - "value": [ - "/var/log/messages*", - "/var/log/syslog*" - ], - "type": "text" - } - }, - "id": "logfile-system.syslog-4243f6b9-6ce2-48ec-859a-b5df4baa7c11", - "compiled_stream": { - "paths": [ - "/var/log/messages*", - "/var/log/syslog*" - ], - "exclude_files": [ - ".gz$" - ], - "multiline": { - "pattern": "^\\s", - "match": "after" - }, - "processors": [ - { - "add_locale": null - } - ] - } - } - ] - }, - { - "type": "winlog", - "policy_template": "system", - "enabled": true, - "streams": [ - { - "enabled": true, - "data_stream": { - "type": "logs", - "dataset": "system.application" - }, - "vars": { - "event_id": { - "type": "text" - }, - "processors": { - "type": "yaml" - }, - "tags": { - "value": [], - "type": "text" - } - }, - "id": "winlog-system.application-4243f6b9-6ce2-48ec-859a-b5df4baa7c11", - "compiled_stream": { - "name": "Application", - "condition": "${host.platform} == 'windows'", - "ignore_older": "72h", - "tags": null - } - }, - { - "enabled": true, - "data_stream": { - "type": "logs", - "dataset": "system.security" - }, - "vars": { - "event_id": { - "type": "text" - }, - "processors": { - "type": "yaml" - }, - "tags": { - "value": [], - "type": "text" - } - }, - "id": "winlog-system.security-4243f6b9-6ce2-48ec-859a-b5df4baa7c11", - "compiled_stream": { - "name": "Security", - "condition": "${host.platform} == 'windows'", - "tags": null - } - }, - { - "enabled": true, - "data_stream": { - "type": "logs", - "dataset": "system.system" - }, - "vars": { - "event_id": { - "type": "text" - }, - "processors": { - "type": "yaml" - }, - "tags": { - "value": [], - "type": "text" - } - }, - "id": "winlog-system.system-4243f6b9-6ce2-48ec-859a-b5df4baa7c11", - "compiled_stream": { - "name": "System", - "condition": "${host.platform} == 'windows'", - "tags": null - } - } - ], - "vars": { - "preserve_original_event": { - "value": false, - "type": "bool" - } - } - }, - { - "type": "system/metrics", - "policy_template": "system", - "enabled": true, - "streams": [ - { - "enabled": false, - "data_stream": { - "type": "metrics", - "dataset": "system.core" - }, - "vars": { - "period": { - "value": "10s", - "type": "text" - }, - "core.metrics": { - "value": [ - "percentages" - ], - "type": "text" - } - }, - "id": "system/metrics-system.core-4243f6b9-6ce2-48ec-859a-b5df4baa7c11" - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.cpu" - }, - "vars": { - "period": { - "value": "10s", - "type": "text" - }, - "cpu.metrics": { - "value": [ - "percentages", - "normalized_percentages" - ], - "type": "text" - } - }, - "id": "system/metrics-system.cpu-4243f6b9-6ce2-48ec-859a-b5df4baa7c11", - "compiled_stream": { - "metricsets": [ - "cpu" - ], - "cpu.metrics": [ - "percentages", - "normalized_percentages" - ], - "period": "10s" - } - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.diskio" - }, - "vars": { - "period": { - "value": "10s", - "type": "text" - }, - "diskio.include_devices": { - "value": [], - "type": "text" - } - }, - "id": "system/metrics-system.diskio-4243f6b9-6ce2-48ec-859a-b5df4baa7c11", - "compiled_stream": { - "metricsets": [ - "diskio" - ], - "diskio.include_devices": null, - "period": "10s" - } - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.filesystem" - }, - "vars": { - "period": { - "value": "1m", - "type": "text" - }, - "processors": { - "value": "- drop_event.when.regexp:\n system.filesystem.mount_point: ^/(sys|cgroup|proc|dev|etc|host|lib|snap)($|/)\n", - "type": "yaml" - } - }, - "id": "system/metrics-system.filesystem-4243f6b9-6ce2-48ec-859a-b5df4baa7c11", - "compiled_stream": { - "metricsets": [ - "filesystem" - ], - "period": "1m", - "processors": [ - { - "drop_event.when.regexp": { - "system.filesystem.mount_point": "^/(sys|cgroup|proc|dev|etc|host|lib|snap)($|/)" - } - } - ] - } - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.fsstat" - }, - "vars": { - "period": { - "value": "1m", - "type": "text" - }, - "processors": { - "value": "- drop_event.when.regexp:\n system.fsstat.mount_point: ^/(sys|cgroup|proc|dev|etc|host|lib|snap)($|/)\n", - "type": "yaml" - } - }, - "id": "system/metrics-system.fsstat-4243f6b9-6ce2-48ec-859a-b5df4baa7c11", - "compiled_stream": { - "metricsets": [ - "fsstat" - ], - "period": "1m", - "processors": [ - { - "drop_event.when.regexp": { - "system.fsstat.mount_point": "^/(sys|cgroup|proc|dev|etc|host|lib|snap)($|/)" - } - } - ] - } - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.load" - }, - "vars": { - "period": { - "value": "10s", - "type": "text" - } - }, - "id": "system/metrics-system.load-4243f6b9-6ce2-48ec-859a-b5df4baa7c11", - "compiled_stream": { - "metricsets": [ - "load" - ], - "condition": "${host.platform} != 'windows'", - "period": "10s" - } - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.memory" - }, - "vars": { - "period": { - "value": "10s", - "type": "text" - } - }, - "id": "system/metrics-system.memory-4243f6b9-6ce2-48ec-859a-b5df4baa7c11", - "compiled_stream": { - "metricsets": [ - "memory" - ], - "period": "10s" - } - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.network" - }, - "vars": { - "period": { - "value": "10s", - "type": "text" - }, - "network.interfaces": { - "value": [], - "type": "text" - } - }, - "id": "system/metrics-system.network-4243f6b9-6ce2-48ec-859a-b5df4baa7c11", - "compiled_stream": { - "metricsets": [ - "network" - ], - "period": "10s", - "network.interfaces": null - } - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.process" - }, - "vars": { - "period": { - "value": "10s", - "type": "text" - }, - "process.include_top_n.by_cpu": { - "value": 5, - "type": "integer" - }, - "process.include_top_n.by_memory": { - "value": 5, - "type": "integer" - }, - "process.cmdline.cache.enabled": { - "value": true, - "type": "bool" - }, - "process.cgroups.enabled": { - "value": false, - "type": "bool" - }, - "process.env.whitelist": { - "value": [], - "type": "text" - }, - "process.include_cpu_ticks": { - "value": false, - "type": "bool" - }, - "processes": { - "value": [ - ".*" - ], - "type": "text" - } - }, - "id": "system/metrics-system.process-4243f6b9-6ce2-48ec-859a-b5df4baa7c11", - "compiled_stream": { - "metricsets": [ - "process" - ], - "period": "10s", - "process.include_top_n.by_cpu": 5, - "process.include_top_n.by_memory": 5, - "process.cmdline.cache.enabled": true, - "process.cgroups.enabled": false, - "process.include_cpu_ticks": false, - "processes": [ - ".*" - ] - } - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.process.summary" - }, - "vars": { - "period": { - "value": "10s", - "type": "text" - } - }, - "id": "system/metrics-system.process.summary-4243f6b9-6ce2-48ec-859a-b5df4baa7c11", - "compiled_stream": { - "metricsets": [ - "process_summary" - ], - "period": "10s" - } - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.socket_summary" - }, - "vars": { - "period": { - "value": "10s", - "type": "text" - } - }, - "id": "system/metrics-system.socket_summary-4243f6b9-6ce2-48ec-859a-b5df4baa7c11", - "compiled_stream": { - "metricsets": [ - "socket_summary" - ], - "period": "10s" - } - }, - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "system.uptime" - }, - "vars": { - "period": { - "value": "10s", - "type": "text" - } - }, - "id": "system/metrics-system.uptime-4243f6b9-6ce2-48ec-859a-b5df4baa7c11", - "compiled_stream": { - "metricsets": [ - "uptime" - ], - "period": "10s" - } - } - ], - "vars": { - "system.hostfs": { - "type": "text" - } - } - }, - { - "type": "httpjson", - "policy_template": "system", - "enabled": false, - "streams": [ - { - "enabled": false, - "data_stream": { - "type": "logs", - "dataset": "system.application" - }, - "vars": { - "interval": { - "value": "10s", - "type": "text" - }, - "search": { - "value": "search sourcetype=\"XmlWinEventLog:Application\"", - "type": "text" - }, - "tags": { - "value": [ - "forwarded" - ], - "type": "text" - } - }, - "id": "httpjson-system.application-4243f6b9-6ce2-48ec-859a-b5df4baa7c11" - }, - { - "enabled": false, - "data_stream": { - "type": "logs", - "dataset": "system.security" - }, - "vars": { - "interval": { - "value": "10s", - "type": "text" - }, - "search": { - "value": "search sourcetype=\"XmlWinEventLog:Security\"", - "type": "text" - }, - "tags": { - "value": [ - "forwarded" - ], - "type": "text" - } - }, - "id": "httpjson-system.security-4243f6b9-6ce2-48ec-859a-b5df4baa7c11" - }, - { - "enabled": false, - "data_stream": { - "type": "logs", - "dataset": "system.system" - }, - "vars": { - "interval": { - "value": "10s", - "type": "text" - }, - "search": { - "value": "search sourcetype=\"XmlWinEventLog:System\"", - "type": "text" - }, - "tags": { - "value": [ - "forwarded" - ], - "type": "text" - } - }, - "id": "httpjson-system.system-4243f6b9-6ce2-48ec-859a-b5df4baa7c11" - } - ], - "vars": { - "url": { - "value": "https://server.example.com:8089", - "type": "text" - }, - "username": { - "type": "text" - }, - "password": { - "type": "password" - }, - "token": { - "type": "password" - }, - "preserve_original_event": { - "value": false, - "type": "bool" - }, - "ssl": { - "value": "#certificate_authorities:\n# - |\n# -----BEGIN CERTIFICATE-----\n# MIIDCjCCAfKgAwIBAgITJ706Mu2wJlKckpIvkWxEHvEyijANBgkqhkiG9w0BAQsF\n# ADAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwIBcNMTkwNzIyMTkyOTA0WhgPMjExOTA2\n# MjgxOTI5MDRaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEB\n# BQADggEPADCCAQoCggEBANce58Y/JykI58iyOXpxGfw0/gMvF0hUQAcUrSMxEO6n\n# fZRA49b4OV4SwWmA3395uL2eB2NB8y8qdQ9muXUdPBWE4l9rMZ6gmfu90N5B5uEl\n# 94NcfBfYOKi1fJQ9i7WKhTjlRkMCgBkWPkUokvBZFRt8RtF7zI77BSEorHGQCk9t\n# /D7BS0GJyfVEhftbWcFEAG3VRcoMhF7kUzYwp+qESoriFRYLeDWv68ZOvG7eoWnP\n# PsvZStEVEimjvK5NSESEQa9xWyJOmlOKXhkdymtcUd/nXnx6UTCFgnkgzSdTWV41\n# CI6B6aJ9svCTI2QuoIq2HxX/ix7OvW1huVmcyHVxyUECAwEAAaNTMFEwHQYDVR0O\n# BBYEFPwN1OceFGm9v6ux8G+DZ3TUDYxqMB8GA1UdIwQYMBaAFPwN1OceFGm9v6ux\n# 8G+DZ3TUDYxqMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG5D\n# 874A4YI7YUwOVsVAdbWtgp1d0zKcPRR+r2OdSbTAV5/gcS3jgBJ3i1BN34JuDVFw\n# 3DeJSYT3nxy2Y56lLnxDeF8CUTUtVQx3CuGkRg1ouGAHpO/6OqOhwLLorEmxi7tA\n# H2O8mtT0poX5AnOAhzVy7QW0D/k4WaoLyckM5hUa6RtvgvLxOwA0U+VGurCDoctu\n# 8F4QOgTAWyh8EZIwaKCliFRSynDpv3JTUwtfZkxo6K6nce1RhCWFAsMvDZL8Dgc0\n# yvgJ38BRsFOtkRuAGSf6ZUwTO8JJRRIFnpUzXflAnGivK9M13D5GEQMmIl6U9Pvk\n# sxSmbIUfc2SGJGCJD4I=\n# -----END CERTIFICATE-----\n", - "type": "yaml" - } - } - } - ], - "revision": 1, - "created_at": "2021-09-30T10:02:48.904Z", - "created_by": "system", - "updated_at": "2021-09-30T10:02:48.904Z", - "updated_by": "system" - } - ] - } -} \ No newline at end of file diff --git a/x-pack/plugins/fleet/cypress/fixtures/integrations/apache.json b/x-pack/plugins/fleet/cypress/fixtures/integrations/apache.json deleted file mode 100644 index b8c4c31767f3d..0000000000000 --- a/x-pack/plugins/fleet/cypress/fixtures/integrations/apache.json +++ /dev/null @@ -1,1060 +0,0 @@ -{ - "item": { - "name": "apache", - "title": "Apache", - "version": "1.1.0", - "release": "ga", - "description": "This Elastic integration collects logs and metrics from Apache servers", - "type": "integration", - "download": "/epr/apache/apache-1.1.0.zip", - "path": "/package/apache/1.1.0", - "icons": [ - { - "src": "/img/logo_apache.svg", - "path": "/package/apache/1.1.0/img/logo_apache.svg", - "title": "Apache Logo", - "size": "32x32", - "type": "image/svg+xml" - } - ], - "format_version": "1.0.0", - "readme": "/package/apache/1.1.0/docs/README.md", - "license": "basic", - "categories": [ - "web" - ], - "conditions": { - "kibana.version": "^7.14.0" - }, - "screenshots": [ - { - "src": "/img/apache-metrics-overview.png", - "path": "/package/apache/1.1.0/img/apache-metrics-overview.png", - "title": "Apache metrics overview", - "size": "3360x3064", - "type": "image/png" - }, - { - "src": "/img/apache-logs-overview.png", - "path": "/package/apache/1.1.0/img/apache-logs-overview.png", - "title": "Apache logs overview", - "size": "3342x1384", - "type": "image/png" - } - ], - "assets": { - "kibana": { - "dashboard": [ - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "dashboard", - "file": "apache-Logs-Apache-Dashboard.json", - "path": "apache-1.1.0/kibana/dashboard/apache-Logs-Apache-Dashboard.json" - }, - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "dashboard", - "file": "apache-Metrics-Apache-HTTPD-server-status.json", - "path": "apache-1.1.0/kibana/dashboard/apache-Metrics-Apache-HTTPD-server-status.json" - } - ], - "ml_module": [ - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "ml_module", - "file": "apache-Logs-ml.json", - "path": "apache-1.1.0/kibana/ml_module/apache-Logs-ml.json" - } - ], - "search": [ - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "search", - "file": "apache-HTTPD.json", - "path": "apache-1.1.0/kibana/search/apache-HTTPD.json" - }, - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "search", - "file": "apache-access-logs.json", - "path": "apache-1.1.0/kibana/search/apache-access-logs.json" - }, - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "search", - "file": "apache-errors-log.json", - "path": "apache-1.1.0/kibana/search/apache-errors-log.json" - } - ], - "visualization": [ - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "visualization", - "file": "apache-22057f20-3a12-11eb-8946-296aab7b13db.json", - "path": "apache-1.1.0/kibana/visualization/apache-22057f20-3a12-11eb-8946-296aab7b13db.json" - }, - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "visualization", - "file": "apache-320cd980-3a36-11eb-8946-296aab7b13db.json", - "path": "apache-1.1.0/kibana/visualization/apache-320cd980-3a36-11eb-8946-296aab7b13db.json" - }, - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "visualization", - "file": "apache-47820ce0-3a1d-11eb-8946-296aab7b13db.json", - "path": "apache-1.1.0/kibana/visualization/apache-47820ce0-3a1d-11eb-8946-296aab7b13db.json" - }, - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "visualization", - "file": "apache-7724cf20-3a39-11eb-8946-296aab7b13db.json", - "path": "apache-1.1.0/kibana/visualization/apache-7724cf20-3a39-11eb-8946-296aab7b13db.json" - }, - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "visualization", - "file": "apache-7d68f730-3a39-11eb-8946-296aab7b13db.json", - "path": "apache-1.1.0/kibana/visualization/apache-7d68f730-3a39-11eb-8946-296aab7b13db.json" - }, - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "visualization", - "file": "apache-805d7bb0-3a10-11eb-8946-296aab7b13db.json", - "path": "apache-1.1.0/kibana/visualization/apache-805d7bb0-3a10-11eb-8946-296aab7b13db.json" - }, - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "visualization", - "file": "apache-99666080-3a20-11eb-8946-296aab7b13db.json", - "path": "apache-1.1.0/kibana/visualization/apache-99666080-3a20-11eb-8946-296aab7b13db.json" - }, - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "visualization", - "file": "apache-HTTPD-CPU.json", - "path": "apache-1.1.0/kibana/visualization/apache-HTTPD-CPU.json" - }, - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "visualization", - "file": "apache-HTTPD-Load1-slash-5-slash-15.json", - "path": "apache-1.1.0/kibana/visualization/apache-HTTPD-Load1-slash-5-slash-15.json" - }, - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "visualization", - "file": "apache-HTTPD-Scoreboard.json", - "path": "apache-1.1.0/kibana/visualization/apache-HTTPD-Scoreboard.json" - }, - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "visualization", - "file": "apache-a45311f0-3a34-11eb-8946-296aab7b13db.json", - "path": "apache-1.1.0/kibana/visualization/apache-a45311f0-3a34-11eb-8946-296aab7b13db.json" - }, - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "visualization", - "file": "apache-access-unique-IPs-map.json", - "path": "apache-1.1.0/kibana/visualization/apache-access-unique-IPs-map.json" - }, - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "visualization", - "file": "apache-browsers.json", - "path": "apache-1.1.0/kibana/visualization/apache-browsers.json" - }, - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "visualization", - "file": "apache-ed44f820-3a10-11eb-8946-296aab7b13db.json", - "path": "apache-1.1.0/kibana/visualization/apache-ed44f820-3a10-11eb-8946-296aab7b13db.json" - }, - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "visualization", - "file": "apache-error-logs-over-time.json", - "path": "apache-1.1.0/kibana/visualization/apache-error-logs-over-time.json" - }, - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "visualization", - "file": "apache-f4ffec70-3a36-11eb-8946-296aab7b13db.json", - "path": "apache-1.1.0/kibana/visualization/apache-f4ffec70-3a36-11eb-8946-296aab7b13db.json" - }, - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "visualization", - "file": "apache-operating-systems.json", - "path": "apache-1.1.0/kibana/visualization/apache-operating-systems.json" - }, - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "visualization", - "file": "apache-response-codes-of-top-URLs.json", - "path": "apache-1.1.0/kibana/visualization/apache-response-codes-of-top-URLs.json" - }, - { - "pkgkey": "apache-1.1.0", - "service": "kibana", - "type": "visualization", - "file": "apache-response-codes-over-time.json", - "path": "apache-1.1.0/kibana/visualization/apache-response-codes-over-time.json" - } - ] - }, - "elasticsearch": { - "ingest_pipeline": [ - { - "pkgkey": "apache-1.1.0", - "service": "elasticsearch", - "type": "ingest_pipeline", - "file": "default.yml", - "dataset": "access", - "path": "apache-1.1.0/data_stream/access/elasticsearch/ingest_pipeline/default.yml" - }, - { - "pkgkey": "apache-1.1.0", - "service": "elasticsearch", - "type": "ingest_pipeline", - "file": "third-party.yml", - "dataset": "access", - "path": "apache-1.1.0/data_stream/access/elasticsearch/ingest_pipeline/third-party.yml" - }, - { - "pkgkey": "apache-1.1.0", - "service": "elasticsearch", - "type": "ingest_pipeline", - "file": "default.yml", - "dataset": "error", - "path": "apache-1.1.0/data_stream/error/elasticsearch/ingest_pipeline/default.yml" - }, - { - "pkgkey": "apache-1.1.0", - "service": "elasticsearch", - "type": "ingest_pipeline", - "file": "third-party.yml", - "dataset": "error", - "path": "apache-1.1.0/data_stream/error/elasticsearch/ingest_pipeline/third-party.yml" - } - ] - } - }, - "policy_templates": [ - { - "name": "apache", - "title": "Apache logs and metrics", - "description": "Collect logs and metrics from Apache instances", - "inputs": [ - { - "type": "logfile", - "title": "Collect logs from Apache instances", - "description": "Collecting Apache access and error logs" - }, - { - "type": "httpjson", - "vars": [ - { - "name": "url", - "type": "text", - "title": "URL of Splunk Enterprise Server", - "description": "i.e. scheme://host:port, path is automatic", - "multi": false, - "required": true, - "show_user": true, - "default": "https://server.example.com:8089" - }, - { - "name": "username", - "type": "text", - "title": "Splunk REST API Username", - "multi": false, - "required": false, - "show_user": true - }, - { - "name": "password", - "type": "password", - "title": "Splunk REST API Password", - "multi": false, - "required": false, - "show_user": true - }, - { - "name": "token", - "type": "password", - "title": "Splunk Authorization Token", - "description": "Bearer Token or Session Key, e.g. \"Bearer eyJFd3e46...\"\nor \"Splunk 192fd3e...\". Cannot be used with username\nand password.\n", - "multi": false, - "required": false, - "show_user": true - }, - { - "name": "ssl", - "type": "yaml", - "title": "SSL Configuration", - "description": "i.e. certificate_authorities, supported_protocols, verification_mode etc.", - "multi": false, - "required": false, - "show_user": false, - "default": "#certificate_authorities:\n# - |\n# -----BEGIN CERTIFICATE-----\n# MIIDCjCCAfKgAwIBAgITJ706Mu2wJlKckpIvkWxEHvEyijANBgkqhkiG9w0BAQsF\n# ADAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwIBcNMTkwNzIyMTkyOTA0WhgPMjExOTA2\n# MjgxOTI5MDRaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEB\n# BQADggEPADCCAQoCggEBANce58Y/JykI58iyOXpxGfw0/gMvF0hUQAcUrSMxEO6n\n# fZRA49b4OV4SwWmA3395uL2eB2NB8y8qdQ9muXUdPBWE4l9rMZ6gmfu90N5B5uEl\n# 94NcfBfYOKi1fJQ9i7WKhTjlRkMCgBkWPkUokvBZFRt8RtF7zI77BSEorHGQCk9t\n# /D7BS0GJyfVEhftbWcFEAG3VRcoMhF7kUzYwp+qESoriFRYLeDWv68ZOvG7eoWnP\n# PsvZStEVEimjvK5NSESEQa9xWyJOmlOKXhkdymtcUd/nXnx6UTCFgnkgzSdTWV41\n# CI6B6aJ9svCTI2QuoIq2HxX/ix7OvW1huVmcyHVxyUECAwEAAaNTMFEwHQYDVR0O\n# BBYEFPwN1OceFGm9v6ux8G+DZ3TUDYxqMB8GA1UdIwQYMBaAFPwN1OceFGm9v6ux\n# 8G+DZ3TUDYxqMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG5D\n# 874A4YI7YUwOVsVAdbWtgp1d0zKcPRR+r2OdSbTAV5/gcS3jgBJ3i1BN34JuDVFw\n# 3DeJSYT3nxy2Y56lLnxDeF8CUTUtVQx3CuGkRg1ouGAHpO/6OqOhwLLorEmxi7tA\n# H2O8mtT0poX5AnOAhzVy7QW0D/k4WaoLyckM5hUa6RtvgvLxOwA0U+VGurCDoctu\n# 8F4QOgTAWyh8EZIwaKCliFRSynDpv3JTUwtfZkxo6K6nce1RhCWFAsMvDZL8Dgc0\n# yvgJ38BRsFOtkRuAGSf6ZUwTO8JJRRIFnpUzXflAnGivK9M13D5GEQMmIl6U9Pvk\n# sxSmbIUfc2SGJGCJD4I=\n# -----END CERTIFICATE-----\n" - } - ], - "title": "Collect logs from third-party REST API (experimental)", - "description": "Collect logs from third-party REST API (experimental)" - }, - { - "type": "apache/metrics", - "vars": [ - { - "name": "hosts", - "type": "text", - "title": "Hosts", - "multi": true, - "required": true, - "show_user": true, - "default": [ - "http://127.0.0.1" - ] - } - ], - "title": "Collect metrics from Apache instances", - "description": "Collecting Apache status metrics" - } - ], - "multiple": true - } - ], - "data_streams": [ - { - "type": "logs", - "dataset": "apache.access", - "title": "Apache access logs", - "release": "experimental", - "ingest_pipeline": "default", - "streams": [ - { - "input": "logfile", - "vars": [ - { - "name": "paths", - "type": "text", - "title": "Paths", - "multi": true, - "required": true, - "show_user": true, - "default": [ - "/var/log/apache2/access.log*", - "/var/log/apache2/other_vhosts_access.log*", - "/var/log/httpd/access_log*" - ] - }, - { - "name": "tags", - "type": "text", - "title": "Tags", - "multi": true, - "required": true, - "show_user": false, - "default": [ - "apache-access" - ] - }, - { - "name": "preserve_original_event", - "type": "bool", - "title": "Preserve original event", - "description": "Preserves a raw copy of the original event, added to the field `event.original`", - "multi": false, - "required": true, - "show_user": true, - "default": false - }, - { - "name": "processors", - "type": "yaml", - "title": "Processors", - "description": "Processors are used to reduce the number of fields in the exported event or to enhance the event with metadata. This executes in the agent before the logs are parsed. See [Processors](https://www.elastic.co/guide/en/beats/filebeat/current/filtering-and-enhancing-data.html) for details.\n", - "multi": false, - "required": false, - "show_user": false - } - ], - "template_path": "log.yml.hbs", - "title": "Apache access logs", - "description": "Collect Apache access logs", - "enabled": true - }, - { - "input": "httpjson", - "vars": [ - { - "name": "interval", - "type": "text", - "title": "Interval to query Splunk Enterprise REST API", - "description": "Go Duration syntax (eg. 10s)", - "multi": false, - "required": true, - "show_user": true, - "default": "10s" - }, - { - "name": "search", - "type": "text", - "title": "Splunk search string", - "multi": false, - "required": true, - "show_user": true, - "default": "search sourcetype=\"access*\"" - }, - { - "name": "tags", - "type": "text", - "title": "Tags", - "multi": true, - "required": false, - "show_user": false, - "default": [ - "forwarded", - "apache-access" - ] - }, - { - "name": "preserve_original_event", - "type": "bool", - "title": "Preserve original event", - "description": "Preserves a raw copy of the original event, added to the field `event.original`", - "multi": false, - "required": true, - "show_user": true, - "default": false - }, - { - "name": "processors", - "type": "yaml", - "title": "Processors", - "description": "Processors are used to reduce the number of fields in the exported event or to enhance the event with metadata. This executes in the agent before the logs are parsed. See [Processors](https://www.elastic.co/guide/en/beats/filebeat/current/filtering-and-enhancing-data.html) for details.\n", - "multi": false, - "required": false, - "show_user": false - } - ], - "template_path": "httpjson.yml.hbs", - "title": "Apache access logs via Splunk Enterprise REST API", - "description": "Collect apache access logs via Splunk Enterprise REST API", - "enabled": false - } - ], - "package": "apache", - "path": "access" - }, - { - "type": "logs", - "dataset": "apache.error", - "title": "Apache error logs", - "release": "experimental", - "ingest_pipeline": "default", - "streams": [ - { - "input": "logfile", - "vars": [ - { - "name": "paths", - "type": "text", - "title": "Paths", - "multi": true, - "required": true, - "show_user": true, - "default": [ - "/var/log/apache2/error.log*", - "/var/log/httpd/error_log*" - ] - }, - { - "name": "tags", - "type": "text", - "title": "Tags", - "multi": true, - "required": true, - "show_user": false, - "default": [ - "apache-error" - ] - }, - { - "name": "preserve_original_event", - "type": "bool", - "title": "Preserve original event", - "description": "Preserves a raw copy of the original event, added to the field `event.original`", - "multi": false, - "required": true, - "show_user": true, - "default": false - }, - { - "name": "processors", - "type": "yaml", - "title": "Processors", - "description": "Processors are used to reduce the number of fields in the exported event or to enhance the event with metadata. This executes in the agent before the logs are parsed. See [Processors](https://www.elastic.co/guide/en/beats/filebeat/current/filtering-and-enhancing-data.html) for details.\n", - "multi": false, - "required": false, - "show_user": false - } - ], - "template_path": "log.yml.hbs", - "title": "Apache error logs", - "description": "Collect Apache error logs", - "enabled": true - }, - { - "input": "httpjson", - "vars": [ - { - "name": "interval", - "type": "text", - "title": "Interval to query Splunk Enterprise REST API", - "description": "Go Duration syntax (eg. 10s)", - "multi": false, - "required": true, - "show_user": true, - "default": "10s" - }, - { - "name": "search", - "type": "text", - "title": "Splunk search string", - "multi": false, - "required": true, - "show_user": true, - "default": "search sourcetype=apache:error OR sourcetype=apache_error" - }, - { - "name": "tags", - "type": "text", - "title": "Tags", - "multi": true, - "required": false, - "show_user": false, - "default": [ - "forwarded", - "apache-error" - ] - }, - { - "name": "preserve_original_event", - "type": "bool", - "title": "Preserve original event", - "description": "Preserves a raw copy of the original event, added to the field `event.original`", - "multi": false, - "required": true, - "show_user": true, - "default": false - }, - { - "name": "processors", - "type": "yaml", - "title": "Processors", - "description": "Processors are used to reduce the number of fields in the exported event or to enhance the event with metadata. This executes in the agent before the logs are parsed. See [Processors](https://www.elastic.co/guide/en/beats/filebeat/current/filtering-and-enhancing-data.html) for details.\n", - "multi": false, - "required": false, - "show_user": false - } - ], - "template_path": "httpjson.yml.hbs", - "title": "Apache error logs via Splunk Enterprise REST API", - "description": "Collect apache error logs via Splunk Enterprise REST API", - "enabled": false - } - ], - "package": "apache", - "path": "error" - }, - { - "type": "metrics", - "dataset": "apache.status", - "title": "Apache status metrics", - "release": "experimental", - "streams": [ - { - "input": "apache/metrics", - "vars": [ - { - "name": "period", - "type": "text", - "title": "Period", - "multi": false, - "required": true, - "show_user": true, - "default": "30s" - }, - { - "name": "server_status_path", - "type": "text", - "title": "Server Status Path", - "multi": false, - "required": true, - "show_user": false, - "default": "/server-status" - } - ], - "template_path": "stream.yml.hbs", - "title": "Apache status metrics", - "description": "Collect Apache status metrics", - "enabled": true - } - ], - "package": "apache", - "path": "status" - } - ], - "owner": { - "github": "elastic/integrations" - }, - "latestVersion": "1.1.0", - "removable": true, - "status": "installed", - "savedObject": { - "id": "apache", - "type": "epm-packages", - "namespaces": [], - "updated_at": "2021-09-30T10:47:12.961Z", - "version": "WzI1NjgsMV0=", - "attributes": { - "installed_kibana_space_id": "default", - "installed_kibana": [ - { - "id": "apache-Logs-Apache-Dashboard", - "type": "dashboard" - }, - { - "id": "apache-Metrics-Apache-HTTPD-server-status", - "type": "dashboard" - }, - { - "id": "apache-22057f20-3a12-11eb-8946-296aab7b13db", - "type": "visualization" - }, - { - "id": "apache-320cd980-3a36-11eb-8946-296aab7b13db", - "type": "visualization" - }, - { - "id": "apache-47820ce0-3a1d-11eb-8946-296aab7b13db", - "type": "visualization" - }, - { - "id": "apache-7724cf20-3a39-11eb-8946-296aab7b13db", - "type": "visualization" - }, - { - "id": "apache-7d68f730-3a39-11eb-8946-296aab7b13db", - "type": "visualization" - }, - { - "id": "apache-805d7bb0-3a10-11eb-8946-296aab7b13db", - "type": "visualization" - }, - { - "id": "apache-99666080-3a20-11eb-8946-296aab7b13db", - "type": "visualization" - }, - { - "id": "apache-HTTPD-CPU", - "type": "visualization" - }, - { - "id": "apache-HTTPD-Load1-slash-5-slash-15", - "type": "visualization" - }, - { - "id": "apache-HTTPD-Scoreboard", - "type": "visualization" - }, - { - "id": "apache-a45311f0-3a34-11eb-8946-296aab7b13db", - "type": "visualization" - }, - { - "id": "apache-access-unique-IPs-map", - "type": "visualization" - }, - { - "id": "apache-browsers", - "type": "visualization" - }, - { - "id": "apache-ed44f820-3a10-11eb-8946-296aab7b13db", - "type": "visualization" - }, - { - "id": "apache-error-logs-over-time", - "type": "visualization" - }, - { - "id": "apache-f4ffec70-3a36-11eb-8946-296aab7b13db", - "type": "visualization" - }, - { - "id": "apache-operating-systems", - "type": "visualization" - }, - { - "id": "apache-response-codes-of-top-URLs", - "type": "visualization" - }, - { - "id": "apache-response-codes-over-time", - "type": "visualization" - }, - { - "id": "apache-HTTPD", - "type": "search" - }, - { - "id": "apache-access-logs", - "type": "search" - }, - { - "id": "apache-errors-log", - "type": "search" - }, - { - "id": "apache-Logs-ml", - "type": "ml-module" - } - ], - "installed_es": [ - { - "id": "logs-apache.access-1.1.0", - "type": "ingest_pipeline" - }, - { - "id": "logs-apache.access-1.1.0-third-party", - "type": "ingest_pipeline" - }, - { - "id": "logs-apache.error-1.1.0", - "type": "ingest_pipeline" - }, - { - "id": "logs-apache.error-1.1.0-third-party", - "type": "ingest_pipeline" - }, - { - "id": "logs-apache.access", - "type": "index_template" - }, - { - "id": "logs-apache.access@settings", - "type": "component_template" - }, - { - "id": "logs-apache.access@custom", - "type": "component_template" - }, - { - "id": "logs-apache.error", - "type": "index_template" - }, - { - "id": "logs-apache.error@settings", - "type": "component_template" - }, - { - "id": "logs-apache.error@custom", - "type": "component_template" - }, - { - "id": "metrics-apache.status", - "type": "index_template" - }, - { - "id": "metrics-apache.status@settings", - "type": "component_template" - }, - { - "id": "metrics-apache.status@custom", - "type": "component_template" - } - ], - "package_assets": [ - { - "id": "c99057a8-c51a-5795-9e00-b4b09237f780", - "type": "epm-packages-assets" - }, - { - "id": "1388d2c7-254a-5cd4-882d-89b3e8b681cd", - "type": "epm-packages-assets" - }, - { - "id": "c3068bcb-5a74-5044-91f6-c8e99eefb003", - "type": "epm-packages-assets" - }, - { - "id": "4cea5f13-0ec6-5ecc-9012-f2dba2c86fab", - "type": "epm-packages-assets" - }, - { - "id": "6f27b654-fc39-502b-bdda-83ed13e775c1", - "type": "epm-packages-assets" - }, - { - "id": "baa6d518-fa85-530f-9cdc-b0f2207599f8", - "type": "epm-packages-assets" - }, - { - "id": "ea0cfbd9-8173-5429-a83b-6168b2cd4f27", - "type": "epm-packages-assets" - }, - { - "id": "3745632e-1306-5ac6-84ee-0fceae577988", - "type": "epm-packages-assets" - }, - { - "id": "079a3007-eec5-504e-a993-8c489ccc992c", - "type": "epm-packages-assets" - }, - { - "id": "625ba117-a66d-5eba-9172-201e4f03fbf0", - "type": "epm-packages-assets" - }, - { - "id": "f0dd03dd-3dee-51da-881b-425e76966139", - "type": "epm-packages-assets" - }, - { - "id": "c356fb2c-395b-595e-bdf4-51c5750d6efe", - "type": "epm-packages-assets" - }, - { - "id": "861a6d88-8e80-5282-8cc4-b74b13da22f8", - "type": "epm-packages-assets" - }, - { - "id": "49186533-1536-5d2d-a45a-b51a4db1eeca", - "type": "epm-packages-assets" - }, - { - "id": "533a5c29-648c-593c-9444-df3d03c4aae0", - "type": "epm-packages-assets" - }, - { - "id": "9d34d784-f5a7-5213-a711-37bf2af21da5", - "type": "epm-packages-assets" - }, - { - "id": "4d5fa019-7503-5a89-95af-a03227622ecd", - "type": "epm-packages-assets" - }, - { - "id": "edc0c10d-f7f4-5523-8dac-ce9c64aff44d", - "type": "epm-packages-assets" - }, - { - "id": "5792421c-b31c-59a3-891c-1566bc85447b", - "type": "epm-packages-assets" - }, - { - "id": "7a72f59a-27a6-5514-9489-1258de496199", - "type": "epm-packages-assets" - }, - { - "id": "69dffce3-96d1-5c71-b4ae-41b6d61fdd4a", - "type": "epm-packages-assets" - }, - { - "id": "0b971e05-221e-5430-87e6-fbebbc8d4a23", - "type": "epm-packages-assets" - }, - { - "id": "5d7fb7e1-e775-5832-95a7-074d692fb176", - "type": "epm-packages-assets" - }, - { - "id": "4a50c74b-e4ce-511c-badd-54997537b6b8", - "type": "epm-packages-assets" - }, - { - "id": "54e21b74-9ea5-537f-8cce-673b10b8ac39", - "type": "epm-packages-assets" - }, - { - "id": "c9fd9a64-722c-59f7-a686-4d92d4395be0", - "type": "epm-packages-assets" - }, - { - "id": "5a53ca55-23ec-59bc-8d04-be12f1776358", - "type": "epm-packages-assets" - }, - { - "id": "b2652216-a523-5183-8eaa-c26f9ba4bbee", - "type": "epm-packages-assets" - }, - { - "id": "97f717d7-78d6-5b8c-acde-edf80aa27201", - "type": "epm-packages-assets" - }, - { - "id": "6b27939a-1f2a-536d-8d84-560ed372d21a", - "type": "epm-packages-assets" - }, - { - "id": "7d68617a-88b0-5d34-8a98-8f51d3c49568", - "type": "epm-packages-assets" - }, - { - "id": "8e212777-acac-5068-acbb-143e0cbfb3eb", - "type": "epm-packages-assets" - }, - { - "id": "436ed6b2-aa68-55d4-912a-346e14903d7b", - "type": "epm-packages-assets" - }, - { - "id": "5169ccd9-75f9-5d84-8116-2f2bac0dd93f", - "type": "epm-packages-assets" - }, - { - "id": "a36f82fe-4aa0-508f-92e4-e33d779c1ed2", - "type": "epm-packages-assets" - }, - { - "id": "96d9ae25-0ee7-59aa-b8a0-4fbb929cce4a", - "type": "epm-packages-assets" - }, - { - "id": "05e1449f-3723-5d3c-a76f-5e307d88c35b", - "type": "epm-packages-assets" - }, - { - "id": "a0e8abee-4777-5a7f-bb9a-c2c60d49d060", - "type": "epm-packages-assets" - }, - { - "id": "4c77c830-b4e2-5c77-a3dd-941249799ce7", - "type": "epm-packages-assets" - }, - { - "id": "e082c4c2-3215-5fb0-a485-b261a774314e", - "type": "epm-packages-assets" - }, - { - "id": "1f4467ca-6aa9-5fcb-a346-f334e018db3f", - "type": "epm-packages-assets" - }, - { - "id": "fc831e85-d43f-5402-8780-c9fb3b040b34", - "type": "epm-packages-assets" - }, - { - "id": "208cc640-7cb1-5dd0-902e-47d82fe273af", - "type": "epm-packages-assets" - }, - { - "id": "65e211ff-9497-5882-88cc-ebfd79578cff", - "type": "epm-packages-assets" - }, - { - "id": "a6ea40cc-bb98-5039-8d52-151ac69cbfb5", - "type": "epm-packages-assets" - }, - { - "id": "d9e1d1e6-1c31-5164-8805-b8b2249bd8b5", - "type": "epm-packages-assets" - }, - { - "id": "aa843dec-f345-5c94-99e3-8bd2bffb9b4e", - "type": "epm-packages-assets" - }, - { - "id": "2b019917-8d4c-5da9-80b2-5005524a1290", - "type": "epm-packages-assets" - }, - { - "id": "617effde-ae31-5f48-928a-acdf7b6bc0bb", - "type": "epm-packages-assets" - }, - { - "id": "10245259-aff6-5cc9-b60b-9d88a230894e", - "type": "epm-packages-assets" - }, - { - "id": "753a2e77-13fe-5aa8-94a7-08e9357e64f0", - "type": "epm-packages-assets" - }, - { - "id": "4132f76c-78bc-5d70-a7cd-421910242f96", - "type": "epm-packages-assets" - }, - { - "id": "74230ee0-f671-57fc-bf3a-1c1be03acf22", - "type": "epm-packages-assets" - }, - { - "id": "a2465b23-c15e-56f9-acad-e2d5387cae48", - "type": "epm-packages-assets" - }, - { - "id": "94586e3f-78a0-5cf8-b4c2-923f4516153a", - "type": "epm-packages-assets" - }, - { - "id": "7b356571-eb79-541c-ba99-e6fdebf74e98", - "type": "epm-packages-assets" - }, - { - "id": "babd82eb-7317-58c0-a5fc-4d14ca1f2d17", - "type": "epm-packages-assets" - }, - { - "id": "aa68dd98-4844-5162-b96f-e6b5eae5f987", - "type": "epm-packages-assets" - } - ], - "es_index_patterns": { - "access": "logs-apache.access-*", - "error": "logs-apache.error-*", - "status": "metrics-apache.status-*" - }, - "name": "apache", - "version": "1.1.0", - "internal": false, - "removable": true, - "install_version": "1.1.0", - "install_status": "installed", - "install_started_at": "2021-09-30T10:46:58.713Z", - "install_source": "registry" - }, - "references": [], - "migrationVersion": { - "epm-packages": "7.14.1" - }, - "coreMigrationVersion": "8.0.0" - } - } -} \ No newline at end of file diff --git a/x-pack/plugins/fleet/cypress/fixtures/integrations/create_integration_response.json b/x-pack/plugins/fleet/cypress/fixtures/integrations/create_integration_response.json deleted file mode 100644 index 6820aadd01fb1..0000000000000 --- a/x-pack/plugins/fleet/cypress/fixtures/integrations/create_integration_response.json +++ /dev/null @@ -1,255 +0,0 @@ -{ - "item": { - "id": "1", - "version": "WzI4NDAsMV0=", - "name": "apache-1", - "description": "", - "namespace": "default", - "policy_id": "9ced27e0-20ff-11ec-b353-dd9d66c6f483", - "enabled": true, - "output_id": "", - "inputs": [ - { - "type": "logfile", - "policy_template": "apache", - "enabled": true, - "streams": [ - { - "enabled": true, - "data_stream": { - "type": "logs", - "dataset": "apache.access" - }, - "vars": { - "paths": { - "value": [ - "/var/log/apache2/access.log*", - "/var/log/apache2/other_vhosts_access.log*", - "/var/log/httpd/access_log*" - ], - "type": "text" - }, - "tags": { - "value": [ - "apache-access" - ], - "type": "text" - }, - "preserve_original_event": { - "value": false, - "type": "bool" - }, - "processors": { - "type": "yaml" - } - }, - "id": "logfile-apache.access-1c588150-010b-448a-b2b8-820d1b33811e", - "compiled_stream": { - "paths": [ - "/var/log/apache2/access.log*", - "/var/log/apache2/other_vhosts_access.log*", - "/var/log/httpd/access_log*" - ], - "tags": [ - "apache-access" - ], - "exclude_files": [ - ".gz$" - ] - } - }, - { - "enabled": true, - "data_stream": { - "type": "logs", - "dataset": "apache.error" - }, - "vars": { - "paths": { - "value": [ - "/var/log/apache2/error.log*", - "/var/log/httpd/error_log*" - ], - "type": "text" - }, - "tags": { - "value": [ - "apache-error" - ], - "type": "text" - }, - "preserve_original_event": { - "value": false, - "type": "bool" - }, - "processors": { - "type": "yaml" - } - }, - "id": "logfile-apache.error-1c588150-010b-448a-b2b8-820d1b33811e", - "compiled_stream": { - "paths": [ - "/var/log/apache2/error.log*", - "/var/log/httpd/error_log*" - ], - "exclude_files": [ - ".gz$" - ], - "tags": [ - "apache-error" - ], - "processors": [ - { - "add_locale": null - } - ] - } - } - ] - }, - { - "type": "httpjson", - "policy_template": "apache", - "enabled": false, - "streams": [ - { - "enabled": false, - "data_stream": { - "type": "logs", - "dataset": "apache.access" - }, - "vars": { - "interval": { - "value": "10s", - "type": "text" - }, - "search": { - "value": "search sourcetype=\"access*\"", - "type": "text" - }, - "tags": { - "value": [ - "forwarded", - "apache-access" - ], - "type": "text" - }, - "preserve_original_event": { - "value": false, - "type": "bool" - }, - "processors": { - "type": "yaml" - } - }, - "id": "httpjson-apache.access-1c588150-010b-448a-b2b8-820d1b33811e" - }, - { - "enabled": false, - "data_stream": { - "type": "logs", - "dataset": "apache.error" - }, - "vars": { - "interval": { - "value": "10s", - "type": "text" - }, - "search": { - "value": "search sourcetype=apache:error OR sourcetype=apache_error", - "type": "text" - }, - "tags": { - "value": [ - "forwarded", - "apache-error" - ], - "type": "text" - }, - "preserve_original_event": { - "value": false, - "type": "bool" - }, - "processors": { - "type": "yaml" - } - }, - "id": "httpjson-apache.error-1c588150-010b-448a-b2b8-820d1b33811e" - } - ], - "vars": { - "url": { - "value": "https://server.example.com:8089", - "type": "text" - }, - "username": { - "type": "text" - }, - "password": { - "type": "password" - }, - "token": { - "type": "password" - }, - "ssl": { - "value": "#certificate_authorities:\n# - |\n# -----BEGIN CERTIFICATE-----\n# MIIDCjCCAfKgAwIBAgITJ706Mu2wJlKckpIvkWxEHvEyijANBgkqhkiG9w0BAQsF\n# ADAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwIBcNMTkwNzIyMTkyOTA0WhgPMjExOTA2\n# MjgxOTI5MDRaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEB\n# BQADggEPADCCAQoCggEBANce58Y/JykI58iyOXpxGfw0/gMvF0hUQAcUrSMxEO6n\n# fZRA49b4OV4SwWmA3395uL2eB2NB8y8qdQ9muXUdPBWE4l9rMZ6gmfu90N5B5uEl\n# 94NcfBfYOKi1fJQ9i7WKhTjlRkMCgBkWPkUokvBZFRt8RtF7zI77BSEorHGQCk9t\n# /D7BS0GJyfVEhftbWcFEAG3VRcoMhF7kUzYwp+qESoriFRYLeDWv68ZOvG7eoWnP\n# PsvZStEVEimjvK5NSESEQa9xWyJOmlOKXhkdymtcUd/nXnx6UTCFgnkgzSdTWV41\n# CI6B6aJ9svCTI2QuoIq2HxX/ix7OvW1huVmcyHVxyUECAwEAAaNTMFEwHQYDVR0O\n# BBYEFPwN1OceFGm9v6ux8G+DZ3TUDYxqMB8GA1UdIwQYMBaAFPwN1OceFGm9v6ux\n# 8G+DZ3TUDYxqMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG5D\n# 874A4YI7YUwOVsVAdbWtgp1d0zKcPRR+r2OdSbTAV5/gcS3jgBJ3i1BN34JuDVFw\n# 3DeJSYT3nxy2Y56lLnxDeF8CUTUtVQx3CuGkRg1ouGAHpO/6OqOhwLLorEmxi7tA\n# H2O8mtT0poX5AnOAhzVy7QW0D/k4WaoLyckM5hUa6RtvgvLxOwA0U+VGurCDoctu\n# 8F4QOgTAWyh8EZIwaKCliFRSynDpv3JTUwtfZkxo6K6nce1RhCWFAsMvDZL8Dgc0\n# yvgJ38BRsFOtkRuAGSf6ZUwTO8JJRRIFnpUzXflAnGivK9M13D5GEQMmIl6U9Pvk\n# sxSmbIUfc2SGJGCJD4I=\n# -----END CERTIFICATE-----\n", - "type": "yaml" - } - } - }, - { - "type": "apache/metrics", - "policy_template": "apache", - "enabled": true, - "streams": [ - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "apache.status" - }, - "vars": { - "period": { - "value": "30s", - "type": "text" - }, - "server_status_path": { - "value": "/server-status", - "type": "text" - } - }, - "id": "apache/metrics-apache.status-1c588150-010b-448a-b2b8-820d1b33811e", - "compiled_stream": { - "metricsets": [ - "status" - ], - "hosts": [ - "http://127.0.0.1" - ], - "period": "30s", - "server_status_path": "/server-status" - } - } - ], - "vars": { - "hosts": { - "value": [ - "http://127.0.0.1" - ], - "type": "text" - } - } - } - ], - "package": { - "name": "apache", - "title": "Apache", - "version": "1.1.0" - }, - "revision": 1, - "created_at": "2021-09-29T09:12:55.869Z", - "created_by": "elastic", - "updated_at": "2021-09-29T09:12:55.869Z", - "updated_by": "elastic" - } -} \ No newline at end of file diff --git a/x-pack/plugins/fleet/cypress/fixtures/integrations/list.json b/x-pack/plugins/fleet/cypress/fixtures/integrations/list.json deleted file mode 100644 index 73c3ff54c5d95..0000000000000 --- a/x-pack/plugins/fleet/cypress/fixtures/integrations/list.json +++ /dev/null @@ -1,260 +0,0 @@ -{ - "items": [ - { - "id": "1", - "version": "WzczOSwxXQ==", - "name": "apache-1", - "description": "", - "namespace": "default", - "policy_id": "30e16140-2106-11ec-a289-25321523992d", - "enabled": true, - "output_id": "", - "inputs": [ - { - "type": "logfile", - "policy_template": "apache", - "enabled": true, - "streams": [ - { - "enabled": true, - "data_stream": { - "type": "logs", - "dataset": "apache.access" - }, - "vars": { - "paths": { - "value": [ - "/var/log/apache2/access.log*", - "/var/log/apache2/other_vhosts_access.log*", - "/var/log/httpd/access_log*" - ], - "type": "text" - }, - "tags": { - "value": [ - "apache-access" - ], - "type": "text" - }, - "preserve_original_event": { - "value": false, - "type": "bool" - }, - "processors": { - "type": "yaml" - } - }, - "id": "logfile-apache.access-63172a6b-4f00-4376-b5e6-fe9b3f00fc79", - "compiled_stream": { - "paths": [ - "/var/log/apache2/access.log*", - "/var/log/apache2/other_vhosts_access.log*", - "/var/log/httpd/access_log*" - ], - "tags": [ - "apache-access" - ], - "exclude_files": [ - ".gz$" - ] - } - }, - { - "enabled": true, - "data_stream": { - "type": "logs", - "dataset": "apache.error" - }, - "vars": { - "paths": { - "value": [ - "/var/log/apache2/error.log*", - "/var/log/httpd/error_log*" - ], - "type": "text" - }, - "tags": { - "value": [ - "apache-error" - ], - "type": "text" - }, - "preserve_original_event": { - "value": false, - "type": "bool" - }, - "processors": { - "type": "yaml" - } - }, - "id": "logfile-apache.error-63172a6b-4f00-4376-b5e6-fe9b3f00fc79", - "compiled_stream": { - "paths": [ - "/var/log/apache2/error.log*", - "/var/log/httpd/error_log*" - ], - "exclude_files": [ - ".gz$" - ], - "tags": [ - "apache-error" - ], - "processors": [ - { - "add_locale": null - } - ] - } - } - ] - }, - { - "type": "httpjson", - "policy_template": "apache", - "enabled": false, - "streams": [ - { - "enabled": false, - "data_stream": { - "type": "logs", - "dataset": "apache.access" - }, - "vars": { - "interval": { - "value": "10s", - "type": "text" - }, - "search": { - "value": "search sourcetype=\"access*\"", - "type": "text" - }, - "tags": { - "value": [ - "forwarded", - "apache-access" - ], - "type": "text" - }, - "preserve_original_event": { - "value": false, - "type": "bool" - }, - "processors": { - "type": "yaml" - } - }, - "id": "httpjson-apache.access-63172a6b-4f00-4376-b5e6-fe9b3f00fc79" - }, - { - "enabled": false, - "data_stream": { - "type": "logs", - "dataset": "apache.error" - }, - "vars": { - "interval": { - "value": "10s", - "type": "text" - }, - "search": { - "value": "search sourcetype=apache:error OR sourcetype=apache_error", - "type": "text" - }, - "tags": { - "value": [ - "forwarded", - "apache-error" - ], - "type": "text" - }, - "preserve_original_event": { - "value": false, - "type": "bool" - }, - "processors": { - "type": "yaml" - } - }, - "id": "httpjson-apache.error-63172a6b-4f00-4376-b5e6-fe9b3f00fc79" - } - ], - "vars": { - "url": { - "value": "https://server.example.com:8089", - "type": "text" - }, - "username": { - "type": "text" - }, - "password": { - "type": "password" - }, - "token": { - "type": "password" - }, - "ssl": { - "value": "#certificate_authorities:\n# - |\n# -----BEGIN CERTIFICATE-----\n# MIIDCjCCAfKgAwIBAgITJ706Mu2wJlKckpIvkWxEHvEyijANBgkqhkiG9w0BAQsF\n# ADAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwIBcNMTkwNzIyMTkyOTA0WhgPMjExOTA2\n# MjgxOTI5MDRaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEB\n# BQADggEPADCCAQoCggEBANce58Y/JykI58iyOXpxGfw0/gMvF0hUQAcUrSMxEO6n\n# fZRA49b4OV4SwWmA3395uL2eB2NB8y8qdQ9muXUdPBWE4l9rMZ6gmfu90N5B5uEl\n# 94NcfBfYOKi1fJQ9i7WKhTjlRkMCgBkWPkUokvBZFRt8RtF7zI77BSEorHGQCk9t\n# /D7BS0GJyfVEhftbWcFEAG3VRcoMhF7kUzYwp+qESoriFRYLeDWv68ZOvG7eoWnP\n# PsvZStEVEimjvK5NSESEQa9xWyJOmlOKXhkdymtcUd/nXnx6UTCFgnkgzSdTWV41\n# CI6B6aJ9svCTI2QuoIq2HxX/ix7OvW1huVmcyHVxyUECAwEAAaNTMFEwHQYDVR0O\n# BBYEFPwN1OceFGm9v6ux8G+DZ3TUDYxqMB8GA1UdIwQYMBaAFPwN1OceFGm9v6ux\n# 8G+DZ3TUDYxqMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG5D\n# 874A4YI7YUwOVsVAdbWtgp1d0zKcPRR+r2OdSbTAV5/gcS3jgBJ3i1BN34JuDVFw\n# 3DeJSYT3nxy2Y56lLnxDeF8CUTUtVQx3CuGkRg1ouGAHpO/6OqOhwLLorEmxi7tA\n# H2O8mtT0poX5AnOAhzVy7QW0D/k4WaoLyckM5hUa6RtvgvLxOwA0U+VGurCDoctu\n# 8F4QOgTAWyh8EZIwaKCliFRSynDpv3JTUwtfZkxo6K6nce1RhCWFAsMvDZL8Dgc0\n# yvgJ38BRsFOtkRuAGSf6ZUwTO8JJRRIFnpUzXflAnGivK9M13D5GEQMmIl6U9Pvk\n# sxSmbIUfc2SGJGCJD4I=\n# -----END CERTIFICATE-----\n", - "type": "yaml" - } - } - }, - { - "type": "apache/metrics", - "policy_template": "apache", - "enabled": true, - "streams": [ - { - "enabled": true, - "data_stream": { - "type": "metrics", - "dataset": "apache.status" - }, - "vars": { - "period": { - "value": "30s", - "type": "text" - }, - "server_status_path": { - "value": "/server-status", - "type": "text" - } - }, - "id": "apache/metrics-apache.status-63172a6b-4f00-4376-b5e6-fe9b3f00fc79", - "compiled_stream": { - "metricsets": [ - "status" - ], - "hosts": [ - "http://127.0.0.1" - ], - "period": "30s", - "server_status_path": "/server-status" - } - } - ], - "vars": { - "hosts": { - "value": [ - "http://127.0.0.1" - ], - "type": "text" - } - } - } - ], - "package": { - "name": "apache", - "title": "Apache", - "version": "1.1.0" - }, - "revision": 1, - "created_at": "2021-09-29T09:52:12.865Z", - "created_by": "elastic", - "updated_at": "2021-09-29T09:52:12.865Z", - "updated_by": "elastic" - } - ], - "total": 1, - "page": 1, - "perPage": 20 -} \ No newline at end of file diff --git a/x-pack/plugins/fleet/cypress/integration/agent_policy.spec.ts b/x-pack/plugins/fleet/cypress/integration/agent_policy.spec.ts new file mode 100644 index 0000000000000..694cf6c1527ae --- /dev/null +++ b/x-pack/plugins/fleet/cypress/integration/agent_policy.spec.ts @@ -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. + */ + +describe('Edit agent policy', () => { + beforeEach(() => { + cy.intercept('/api/fleet/agent_policies/policy-1', { + item: { + id: 'policy-1', + name: 'Agent policy 1', + description: '', + namespace: 'default', + monitoring_enabled: ['logs', 'metrics'], + status: 'active', + }, + }); + cy.intercept('/api/fleet/agent_status?policyId=policy-1', { + results: { + total: 0, + inactive: 0, + online: 0, + error: 0, + offline: 0, + updating: 0, + other: 0, + events: 0, + }, + }); + }); + + it('should edit agent policy', () => { + cy.visit('/app/fleet/policies/policy-1/settings'); + cy.getBySel('toastCloseButton').click(); + cy.get('[placeholder="Optional description"').clear().type('desc'); + + cy.intercept('/api/fleet/agent_policies/policy-1', { + item: { + id: 'policy-1', + name: 'Agent policy 1', + description: 'desc', + namespace: 'default', + monitoring_enabled: ['logs', 'metrics'], + status: 'active', + }, + }); + cy.intercept('PUT', '/api/fleet/agent_policies/policy-1', { + name: 'Agent policy 1', + description: 'desc', + namespace: 'default', + monitoring_enabled: ['logs', 'metrics'], + }).as('updateAgentPolicy'); + cy.get('.euiButton').contains('Save changes').click(); + + cy.wait('@updateAgentPolicy').then((interception) => { + expect(interception.request.body.description).to.equal('desc'); + }); + }); +}); diff --git a/x-pack/plugins/fleet/cypress/integration/fleet_settings.spec.ts b/x-pack/plugins/fleet/cypress/integration/fleet_settings.spec.ts new file mode 100644 index 0000000000000..ab4bf6b4a66a9 --- /dev/null +++ b/x-pack/plugins/fleet/cypress/integration/fleet_settings.spec.ts @@ -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 { CONFIRM_MODAL_BTN } from '../screens/integrations'; + +describe('Edit settings', () => { + beforeEach(() => { + cy.intercept('/api/fleet/settings', { + item: { id: 'fleet-default-settings', fleet_server_hosts: [] }, + }); + cy.intercept('/api/fleet/outputs', { + items: [ + { + id: 'fleet-default-output', + name: 'default', + type: 'elasticsearch', + is_default: true, + is_default_monitoring: true, + }, + ], + }); + + cy.visit('/app/fleet/settings'); + cy.getBySel('toastCloseButton').click(); + }); + + it('should update hosts', () => { + cy.getBySel('editHostsBtn').click(); + cy.get('[placeholder="Specify host URL"').type('http://localhost:8220'); + + cy.intercept('/api/fleet/settings', { + item: { id: 'fleet-default-settings', fleet_server_hosts: ['http://localhost:8220'] }, + }); + cy.intercept('PUT', '/api/fleet/settings', { + fleet_server_hosts: ['http://localhost:8220'], + }).as('updateSettings'); + + cy.getBySel('saveApplySettingsBtn').click(); + cy.getBySel(CONFIRM_MODAL_BTN).click(); + + cy.wait('@updateSettings').then((interception) => { + expect(interception.request.body.fleet_server_hosts[0]).to.equal('http://localhost:8220'); + }); + }); + + it('should update outputs', () => { + cy.getBySel('editOutputBtn').click(); + cy.get('[placeholder="Specify name"').clear().type('output-1'); + + cy.intercept('/api/fleet/outputs', { + items: [ + { + id: 'fleet-default-output', + name: 'output-1', + type: 'elasticsearch', + is_default: true, + is_default_monitoring: true, + }, + ], + }); + cy.intercept('PUT', '/api/fleet/outputs/fleet-default-output', { + name: 'output-1', + type: 'elasticsearch', + is_default: true, + is_default_monitoring: true, + }).as('updateOutputs'); + + cy.getBySel('saveApplySettingsBtn').click(); + cy.getBySel(CONFIRM_MODAL_BTN).click(); + + cy.wait('@updateOutputs').then((interception) => { + expect(interception.request.body.name).to.equal('output-1'); + }); + }); +}); diff --git a/x-pack/plugins/fleet/cypress/integration/fleet_startup.spec.ts b/x-pack/plugins/fleet/cypress/integration/fleet_startup.spec.ts index 804fe56510c1d..83423d62e2a43 100644 --- a/x-pack/plugins/fleet/cypress/integration/fleet_startup.spec.ts +++ b/x-pack/plugins/fleet/cypress/integration/fleet_startup.spec.ts @@ -5,29 +5,123 @@ * 2.0. */ -import { ADD_AGENT_BUTTON, AGENT_POLICIES_TAB, ENROLLMENT_TOKENS_TAB } from '../screens/fleet'; +import { AGENTS_TAB, AGENT_POLICIES_TAB, ENROLLMENT_TOKENS_TAB } from '../screens/fleet'; +import { cleanupAgentPolicies, unenrollAgent } from '../tasks/cleanup'; import { FLEET, navigateTo } from '../tasks/navigation'; describe('Fleet startup', () => { - before(() => { - navigateTo(FLEET); - }); + function navigateToTab(tab: string) { + cy.getBySel(tab).click(); + cy.get('.euiBasicTable-loading').should('not.exist'); + } - it('should display Add agent button and Healthy agent once Fleet Agent page loaded', () => { - cy.getBySel(ADD_AGENT_BUTTON).contains('Add agent'); - cy.get('.euiBadge').contains('Healthy'); - }); + function navigateToAgentPolicy(name: string) { + cy.get('.euiLink').contains(name).click(); + cy.get('.euiLoadingSpinner').should('not.exist'); + } + + function navigateToEnrollmentTokens() { + cy.getBySel(ENROLLMENT_TOKENS_TAB).click(); + cy.get('.euiBasicTable-loading').should('not.exist'); + cy.get('.euiButtonIcon--danger'); // wait for trash icon + } + + function verifyPolicy(name: string, integrations: string[]) { + navigateToTab(AGENT_POLICIES_TAB); + + navigateToAgentPolicy(name); + integrations.forEach((integration) => { + cy.get('.euiLink').contains(integration); + }); + + cy.get('.euiButtonEmpty').contains('View all agent policies').click(); - it('should display default agent policies on agent policies tab', () => { - cy.getBySel(AGENT_POLICIES_TAB).click(); - cy.get('.euiLink').contains('Default policy'); - cy.get('.euiLink').contains('Default Fleet Server policy'); + navigateToEnrollmentTokens(); + + cy.get('.euiTableCellContent').contains(name); + } + + function verifyAgentPackage() { + cy.visit('/app/integrations/installed'); + cy.getBySel('integration-card:epr:elastic_agent'); + } + + // skipping Fleet Server enroll, to enable, comment out runner.ts line 23 + describe.skip('Fleet Server', () => { + it('should display Add agent button and Healthy agent once Fleet Agent page loaded', () => { + navigateTo(FLEET); + cy.get('.euiBadge').contains('Healthy'); + + verifyPolicy('Fleet Server policy', ['Fleet Server']); + }); + + after(() => { + unenrollAgent(); + cleanupAgentPolicies(); + }); }); - it('should display default tokens on enrollment tokens tab', () => { - cy.getBySel(ENROLLMENT_TOKENS_TAB).click(); - cy.get('.euiTableRow').should('have.length', 2); - cy.get('.euiTableRowCell').contains('Default policy'); - cy.get('.euiTableRowCell').contains('Default Fleet Server policy'); + describe('Create policies', () => { + after(() => { + cleanupAgentPolicies(); + }); + + beforeEach(() => { + navigateTo(FLEET); + }); + + it('should have no agent policy by default', () => { + cy.request('/api/fleet/agent_policies?full=true').then((response: any) => { + expect(response.body.items.length).to.equal(0); + }); + }); + + it('should create agent policy', () => { + cy.getBySel('addAgentBtnTop').click(); + cy.getBySel('standaloneTab').click(); + + cy.intercept('POST', '/api/fleet/agent_policies?sys_monitoring=true').as('createAgentPolicy'); + + cy.getBySel('createPolicyBtn').click(); + + let agentPolicyId: string; + const startTime = Date.now(); + cy.wait('@createAgentPolicy', { timeout: 180000 }).then((xhr: any) => { + cy.log('Create agent policy took: ' + (Date.now() - startTime) / 1000 + ' s'); + agentPolicyId = xhr.response.body.item.id; + + cy.getBySel('agentPolicyCreateStatusCallOut').contains('Agent policy created'); + + // verify create button changed to dropdown + cy.getBySel('agentPolicyDropdown'); + // verify agent.yml code block has new policy id + cy.get('.euiCodeBlock__code').contains(`id: ${agentPolicyId}`); + + cy.getBySel('euiFlyoutCloseButton').click(); + + // verify policy is created and has system package + verifyPolicy('Agent policy 1', ['System']); + + verifyAgentPackage(); + }); + }); + + it('should create Fleet Server policy', () => { + cy.getBySel('createFleetServerPolicyBtn').click(); + cy.getBySel('agentPolicyCreateStatusCallOut').contains('Agent policy created'); + + // verify policy is created and has fleet server and system package + verifyPolicy('Fleet Server policy 1', ['Fleet Server', 'System']); + + navigateToTab(AGENTS_TAB); + // verify create button changed to dropdown + cy.getBySel('agentPolicyDropdown'); + + // verify fleet server enroll command contains created policy id + cy.getBySel('fleetServerHostInput').type('http://localhost:8220'); + cy.getBySel('fleetServerAddHostBtn').click(); + cy.getBySel('fleetServerGenerateServiceTokenBtn').click(); + cy.get('.euiCodeBlock__code').contains('--fleet-server-policy=fleet-server-policy'); + }); }); }); diff --git a/x-pack/plugins/fleet/cypress/integration/integrations.spec.ts b/x-pack/plugins/fleet/cypress/integration/integrations.spec.ts deleted file mode 100644 index 8b1a5e97279e8..0000000000000 --- a/x-pack/plugins/fleet/cypress/integration/integrations.spec.ts +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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 { INTEGRATIONS, navigateTo } from '../tasks/navigation'; -import { - addIntegration, - installPackageWithVersion, - deleteIntegrations, - clickIfVisible, -} from '../tasks/integrations'; -import { - CONFIRM_MODAL_BTN, - FLYOUT_CLOSE_BTN_SEL, - INTEGRATIONS_CARD, - INTEGRATION_NAME_LINK, - LATEST_VERSION, - PACKAGE_VERSION, - POLICIES_TAB, - SETTINGS_TAB, - UPDATE_PACKAGE_BTN, -} from '../screens/integrations'; - -describe('Add Integration', () => { - const integration = 'Apache'; - - describe('Real API', () => { - afterEach(() => { - deleteIntegrations(integration); - }); - it('should display Apache integration in the Policies list once installed ', () => { - addAndVerifyIntegration(); - }); - - it('should upgrade policies with integration update', () => { - const oldVersion = '0.3.3'; - installPackageWithVersion('apache', oldVersion); - navigateTo(`app/integrations/detail/apache-${oldVersion}/policies`); - - addIntegration(); - - cy.getBySel(INTEGRATION_NAME_LINK).contains('apache-'); - cy.getBySel(PACKAGE_VERSION).contains(oldVersion); - - clickIfVisible(FLYOUT_CLOSE_BTN_SEL); - - cy.getBySel(SETTINGS_TAB).click(); - cy.getBySel(UPDATE_PACKAGE_BTN).click(); - cy.getBySel(CONFIRM_MODAL_BTN).click(); - - cy.getBySel(LATEST_VERSION).then(($title) => { - const newVersion = $title.text(); - cy.get('#upgradePoliciesCheckbox').should('not.exist'); - cy.getBySel(POLICIES_TAB).click(); - cy.getBySel(PACKAGE_VERSION).contains(oldVersion).should('not.exist'); - cy.getBySel(PACKAGE_VERSION).contains(newVersion); - }); - }); - }); - - function addAndVerifyIntegration() { - cy.intercept('GET', '/api/fleet/epm/packages?*').as('packages'); - navigateTo(INTEGRATIONS); - cy.wait('@packages'); - cy.get('.euiLoadingSpinner').should('not.exist'); - cy.get('input[placeholder="Search for integrations"]').type('Apache'); - cy.get(INTEGRATIONS_CARD).contains(integration).click(); - addIntegration(); - cy.getBySel(INTEGRATION_NAME_LINK).contains('apache-'); - } - - it.skip('[Mocked requests] should display Apache integration in the Policies list once installed ', () => { - cy.intercept('POST', '/api/fleet/package_policies', { - fixture: 'integrations/create_integration_response.json', - }); - cy.intercept( - 'GET', - '/api/fleet/package_policies?page=1&perPage=20&kuery=ingest-package-policies.package.name%3A%20apache', - { fixture: 'integrations/list.json' } - ); - cy.intercept('GET', '/api/fleet/agent_policies?*', { - fixture: 'integrations/agent_policies.json', - }); - cy.intercept('GET', '/api/fleet/agent_policies/30e16140-2106-11ec-a289-25321523992d', { - fixture: 'integrations/agent_policy.json', - }); - // TODO fixture includes 1 package policy, should be empty initially - cy.intercept('GET', '/api/fleet/epm/packages/apache/1.1.0', { - fixture: 'integrations/apache.json', - }); - addAndVerifyIntegration(); - }); -}); diff --git a/x-pack/plugins/fleet/cypress/integration/integrations_mock.spec.ts b/x-pack/plugins/fleet/cypress/integration/integrations_mock.spec.ts new file mode 100644 index 0000000000000..080b01458e18f --- /dev/null +++ b/x-pack/plugins/fleet/cypress/integration/integrations_mock.spec.ts @@ -0,0 +1,151 @@ +/* + * 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 { navigateTo } from '../tasks/navigation'; +import { UPDATE_PACKAGE_BTN } from '../screens/integrations'; + +describe('Add Integration - Mock API', () => { + describe('upgrade package and upgrade package policy', () => { + const oldVersion = '0.3.3'; + const newVersion = '1.3.4'; + beforeEach(() => { + cy.intercept('/api/fleet/epm/packages?experimental=true', { + items: [ + { + name: 'apache', + id: 'apache', + version: newVersion, + savedObject: { attributes: { version: oldVersion } }, + status: 'installed', + }, + ], + }); + + cy.intercept(`/api/fleet/epm/packages/apache/${oldVersion}`, { + item: { + name: 'apache', + version: oldVersion, + latestVersion: newVersion, + status: 'installed', + assets: [], + savedObject: { attributes: { version: oldVersion } }, + }, + }); + cy.intercept('/api/fleet/epm/packages/apache/stats', { response: { agent_policy_count: 1 } }); + cy.intercept('/api/fleet/package_policies?*', { + items: [ + { + name: 'apache-2', + description: '', + namespace: 'default', + policy_id: 'policy-1', + package: { + name: 'apache', + version: oldVersion, + }, + updated_by: 'elastic', + }, + ], + }); + const policyConfig = { + id: 'apache-2', + name: 'apache-2', + namespace: 'default', + package: { name: 'apache', version: oldVersion }, + enabled: true, + policy_id: 'policy-1', + inputs: [], + }; + cy.intercept('/api/fleet/package_policies/apache-2', { + item: policyConfig, + }); + cy.intercept('POST', '/api/fleet/package_policies/upgrade/dryrun', [ + { + name: 'apache-2', + diff: [ + policyConfig, + { ...policyConfig, package: { ...policyConfig.package, version: newVersion } }, + ], + hasErrors: false, + }, + ]); + cy.intercept('POST', `/api/fleet/epm/packages/apache/${newVersion}`, { items: [] }); + const agentPolicy = { + id: 'policy-1', + name: 'Agent policy 1', + description: '', + namespace: 'default', + monitoring_enabled: [], + status: 'active', + package_policies: [{ id: 'apache-2', name: 'apache-2', policy_id: 'policy-1', inputs: [] }], + }; + cy.intercept('/api/fleet/agent_policies?*', { items: [agentPolicy] }); + cy.intercept('/api/fleet/agent_policies/policy-1', { + item: agentPolicy, + }); + }); + + it('should upgrade policies without integration update', () => { + navigateTo(`app/integrations/detail/apache-${oldVersion}/settings`); + cy.get('.euiLoadingSpinner').should('not.exist'); + cy.getBySel('installedVersion').contains(oldVersion); + + cy.get('#upgradePoliciesCheckbox').uncheck({ force: true }); + + cy.intercept(`/api/fleet/epm/packages/apache/${newVersion}`, { + item: { + name: 'apache', + version: newVersion, + latestVersion: newVersion, + status: 'installed', + assets: [], + savedObject: { attributes: { version: newVersion } }, + }, + }).as('updatePackage'); + cy.getBySel(UPDATE_PACKAGE_BTN).click(); + cy.wait('@updatePackage'); + cy.get('#upgradePoliciesCheckbox').should('not.exist'); + cy.getBySel('installedVersion').contains(newVersion); + }); + + it('should upgrade integration policy', () => { + cy.intercept('/api/fleet/epm/packages/apache/*', { + item: { + name: 'apache', + version: newVersion, + latestVersion: newVersion, + status: 'installed', + assets: [], + savedObject: { attributes: { version: newVersion } }, + }, + }); + cy.intercept('/api/fleet/epm/packages/apache/stats', { response: { agent_policy_count: 1 } }); + cy.intercept('PUT', '/api/fleet/package_policies/apache-2', { + item: { + id: 'apache-2', + name: 'apache-2', + namespace: 'default', + package: { name: 'apache', version: newVersion }, + enabled: true, + policy_id: 'policy-1', + inputs: [], + }, + }).as('updateApachePolicy'); + + navigateTo( + '/app/fleet/policies/package-1/upgrade-package-policy/apache-2?from=integrations-policy-list' + ); + + cy.getBySel('toastCloseButton').click(); + cy.getBySel('saveIntegration').click(); + + cy.wait('@updateApachePolicy').then((interception) => { + expect(interception.request.body.package.version).to.equal(newVersion); + }); + }); + }); +}); diff --git a/x-pack/plugins/fleet/cypress/integration/integrations_real.spec.ts b/x-pack/plugins/fleet/cypress/integration/integrations_real.spec.ts new file mode 100644 index 0000000000000..0a35dff62c8db --- /dev/null +++ b/x-pack/plugins/fleet/cypress/integration/integrations_real.spec.ts @@ -0,0 +1,116 @@ +/* + * 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 { INTEGRATIONS, navigateTo } from '../tasks/navigation'; +import { + addIntegration, + installPackageWithVersion, + deleteIntegrations, + clickIfVisible, +} from '../tasks/integrations'; +import { + AGENT_POLICY_NAME_LINK, + CONFIRM_MODAL_BTN, + FLYOUT_CLOSE_BTN_SEL, + INTEGRATIONS_CARD, + INTEGRATION_NAME_LINK, + LATEST_VERSION, + PACKAGE_VERSION, + POLICIES_TAB, + SETTINGS_TAB, + UPDATE_PACKAGE_BTN, +} from '../screens/integrations'; +import { cleanupAgentPolicies } from '../tasks/cleanup'; + +describe('Add Integration - Real API', () => { + const integration = 'Apache'; + + after(() => { + cleanupAgentPolicies(); + }); + + it('should install integration without policy', () => { + cy.visit('/app/integrations/detail/tomcat-1.3.0/settings'); + + cy.get('.euiButton').contains('Install Apache Tomcat assets').click(); + cy.get('.euiCallOut').contains('This action will install 1 assets'); + cy.getBySel(CONFIRM_MODAL_BTN).click(); + + cy.get('.euiLoadingSpinner').should('not.exist'); + cy.getBySel('installedVersion').contains('1.3.0'); + + cy.get('.euiButton').contains('Uninstall Apache Tomcat').click(); + cy.getBySel(CONFIRM_MODAL_BTN).click(); + cy.get('.euiLoadingSpinner').should('not.exist'); + cy.get('.euiButton').contains('Install Apache Tomcat assets'); + }); + + function addAndVerifyIntegration() { + cy.intercept('GET', '/api/fleet/epm/packages?*').as('packages'); + navigateTo(INTEGRATIONS); + cy.wait('@packages'); + cy.get('.euiLoadingSpinner').should('not.exist'); + cy.get('input[placeholder="Search for integrations"]').type('Apache'); + cy.get(INTEGRATIONS_CARD).contains(integration).click(); + addIntegration(); + cy.getBySel(INTEGRATION_NAME_LINK).contains('apache-1'); + } + + afterEach(() => { + deleteIntegrations(integration); + }); + it('should display Apache integration in the Policies list once installed ', () => { + addAndVerifyIntegration(); + cy.getBySel(AGENT_POLICY_NAME_LINK).contains('Agent policy 1'); + }); + + it('should add integration to policy', () => { + cy.request('/api/fleet/agent_policies').then((response: any) => { + const agentPolicyId = response.body.items + .filter((policy: any) => policy.name === 'Agent policy 1') + .map((policy: any) => policy.id); + + cy.visit(`/app/fleet/policies/${agentPolicyId}`); + cy.intercept('GET', '/api/fleet/epm/packages?*').as('packages'); + cy.getBySel('addPackagePolicyButton').click(); + cy.wait('@packages'); + cy.get('.euiLoadingSpinner').should('not.exist'); + cy.get('input[placeholder="Search for integrations"]').type('Apache'); + cy.get(INTEGRATIONS_CARD).contains(integration).click(); + addIntegration({ useExistingPolicy: true }); + cy.get('.euiBasicTable-loading').should('not.exist'); + cy.get('.euiTitle').contains('Agent policy 1'); + clickIfVisible(FLYOUT_CLOSE_BTN_SEL); + cy.get('.euiLink').contains('apache-1'); + }); + }); + + it('should upgrade policies with integration update', () => { + const oldVersion = '0.3.3'; + installPackageWithVersion('apache', oldVersion); + navigateTo(`app/integrations/detail/apache-${oldVersion}/policies`); + + addIntegration({ useExistingPolicy: true }); + + cy.getBySel(INTEGRATION_NAME_LINK).contains('apache-'); + cy.getBySel(PACKAGE_VERSION).contains(oldVersion); + + clickIfVisible(FLYOUT_CLOSE_BTN_SEL); + + cy.getBySel(SETTINGS_TAB).click(); + cy.getBySel(UPDATE_PACKAGE_BTN).click(); + cy.getBySel(CONFIRM_MODAL_BTN).click(); + + cy.getBySel(LATEST_VERSION).then(($title) => { + const newVersion = $title.text(); + cy.get('#upgradePoliciesCheckbox').should('not.exist'); + cy.getBySel(POLICIES_TAB).click(); + cy.getBySel(PACKAGE_VERSION).contains(oldVersion).should('not.exist'); + cy.getBySel(PACKAGE_VERSION).contains(newVersion); + }); + }); +}); diff --git a/x-pack/plugins/fleet/cypress/integration/package_policy.spec.ts b/x-pack/plugins/fleet/cypress/integration/package_policy.spec.ts new file mode 100644 index 0000000000000..507b4edbcfc70 --- /dev/null +++ b/x-pack/plugins/fleet/cypress/integration/package_policy.spec.ts @@ -0,0 +1,85 @@ +/* + * 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. + */ + +describe('Edit package policy', () => { + const policyConfig = { + id: 'policy-1', + name: 'fleet_server-1', + namespace: 'default', + package: { name: 'fleet_server', title: 'Fleet Server', version: '1.1.1' }, + enabled: true, + policy_id: 'fleet-server-policy', + output_id: 'fleet-default-output', + inputs: [ + { + type: 'fleet-server', + policy_template: 'fleet_server', + enabled: true, + streams: [], + vars: { + host: { value: ['0.0.0.0'], type: 'text' }, + port: { value: [8220], type: 'integer' }, + max_connections: { type: 'integer' }, + custom: { value: '', type: 'yaml' }, + }, + compiled_input: { server: { port: 8220, host: '0.0.0.0' } }, + }, + ], + }; + beforeEach(() => { + cy.intercept('/api/fleet/package_policies/policy-1', { + item: policyConfig, + }); + cy.intercept('/api/fleet/agent_status?policyId=fleet-server-policy', { + results: { + total: 0, + inactive: 0, + online: 0, + error: 0, + offline: 0, + updating: 0, + other: 0, + events: 0, + }, + }); + cy.intercept('POST', '/api/fleet/package_policies/upgrade/dryrun', [ + { + name: 'fleet_server-1', + diff: [policyConfig, policyConfig], + hasErrors: false, + }, + ]); + cy.intercept('/api/fleet/agent_policies/fleet-server-policy', { + item: { + id: 'fleet-server-policy', + name: 'Fleet server policy 1', + description: '', + namespace: 'default', + monitoring_enabled: ['logs', 'metrics'], + status: 'active', + package_policies: [{ id: 'policy-1', name: 'fleet_server-1' }], + }, + }); + }); + + it('should edit package policy', () => { + cy.visit('/app/fleet/policies/fleet-server-policy/edit-integration/policy-1'); + cy.getBySel('toastCloseButton').click(); + cy.getBySel('packagePolicyDescriptionInput').clear().type('desc'); + + cy.intercept('PUT', '/api/fleet/package_policies/policy-1', { + name: 'fleet_server-1', + description: 'desc', + namespace: 'default', + }).as('updatePackagePolicy'); + cy.get('.euiButton').contains('Save integration').click(); + + cy.wait('@updatePackagePolicy').then((interception) => { + expect(interception.request.body.description).to.equal('desc'); + }); + }); +}); diff --git a/x-pack/plugins/fleet/cypress/screens/fleet.ts b/x-pack/plugins/fleet/cypress/screens/fleet.ts index 6be51e5ed24bc..4c0bb7cea161e 100644 --- a/x-pack/plugins/fleet/cypress/screens/fleet.ts +++ b/x-pack/plugins/fleet/cypress/screens/fleet.ts @@ -7,5 +7,7 @@ export const ADD_AGENT_BUTTON = 'addAgentButton'; +export const AGENTS_TAB = 'fleet-agents-tab'; export const AGENT_POLICIES_TAB = 'fleet-agent-policies-tab'; export const ENROLLMENT_TOKENS_TAB = 'fleet-enrollment-tokens-tab'; +export const SETTINGS_TAB = 'fleet-settings-tab'; diff --git a/x-pack/plugins/fleet/cypress/screens/integrations.ts b/x-pack/plugins/fleet/cypress/screens/integrations.ts index d42fb904b3224..3c980723cc4df 100644 --- a/x-pack/plugins/fleet/cypress/screens/integrations.ts +++ b/x-pack/plugins/fleet/cypress/screens/integrations.ts @@ -10,6 +10,7 @@ export const CREATE_PACKAGE_POLICY_SAVE_BTN = 'createPackagePolicySaveButton'; export const INTEGRATIONS_CARD = '.euiCard__titleAnchor'; export const INTEGRATION_NAME_LINK = 'integrationNameLink'; +export const AGENT_POLICY_NAME_LINK = 'agentPolicyNameLink'; export const CONFIRM_MODAL_BTN = 'confirmModalConfirmButton'; export const CONFIRM_MODAL_BTN_SEL = `[data-test-subj=${CONFIRM_MODAL_BTN}]`; diff --git a/x-pack/plugins/fleet/cypress/support/index.ts b/x-pack/plugins/fleet/cypress/support/index.ts index f074e424d93c3..a967b40e2345c 100644 --- a/x-pack/plugins/fleet/cypress/support/index.ts +++ b/x-pack/plugins/fleet/cypress/support/index.ts @@ -29,13 +29,13 @@ declare global { // eslint-disable-next-line @typescript-eslint/no-namespace namespace Cypress { interface Chainable { - getBySel(value: string): Chainable; + getBySel(value: string, ...args: any[]): Chainable; } } } function getBySel(selector: string, ...args: any[]) { - return cy.get(`[data-test-subj=${selector}]`, ...args); + return cy.get(`[data-test-subj="${selector}"]`, ...args); } Cypress.Commands.add('getBySel', getBySel); diff --git a/x-pack/plugins/fleet/cypress/tasks/cleanup.ts b/x-pack/plugins/fleet/cypress/tasks/cleanup.ts new file mode 100644 index 0000000000000..e3ab5684777ca --- /dev/null +++ b/x-pack/plugins/fleet/cypress/tasks/cleanup.ts @@ -0,0 +1,36 @@ +/* + * 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. + */ + +export function cleanupAgentPolicies() { + cy.request('/api/fleet/agent_policies').then((response: any) => { + response.body.items + .filter((policy: any) => policy.agents === 0) + .forEach((policy: any) => { + cy.request({ + method: 'POST', + url: '/api/fleet/agent_policies/delete', + body: { agentPolicyId: policy.id }, + headers: { 'kbn-xsrf': 'kibana' }, + }); + }); + }); +} + +export function unenrollAgent() { + cy.request('/api/fleet/agents?page=1&perPage=20&showInactive=false&showUpgradeable=false').then( + (response: any) => { + response.body.items.forEach((agent: any) => { + cy.request({ + method: 'POST', + url: `api/fleet/agents/${agent.id}/unenroll`, + body: { revoke: true }, + headers: { 'kbn-xsrf': 'kibana' }, + }); + }); + } + ); +} diff --git a/x-pack/plugins/fleet/cypress/tasks/integrations.ts b/x-pack/plugins/fleet/cypress/tasks/integrations.ts index e9e3f2613c3e8..7e266dce523d5 100644 --- a/x-pack/plugins/fleet/cypress/tasks/integrations.ts +++ b/x-pack/plugins/fleet/cypress/tasks/integrations.ts @@ -10,11 +10,20 @@ import { CONFIRM_MODAL_BTN, CREATE_PACKAGE_POLICY_SAVE_BTN, FLYOUT_CLOSE_BTN_SEL, - INTEGRATION_NAME_LINK, } from '../screens/integrations'; -export const addIntegration = () => { +export const addIntegration = ({ useExistingPolicy } = { useExistingPolicy: false }) => { cy.getBySel(ADD_POLICY_BTN).click(); + if (useExistingPolicy) { + cy.get('#existing').click(); + } else { + // speeding up creating with unchecking system and agent integration + cy.getBySel('agentPolicyFormSystemMonitoringCheckbox').uncheck({ force: true }); + cy.getBySel('advancedOptionsBtn').find('.euiAccordion__button').click(); + cy.get('*[id^="logs_"]').uncheck({ force: true }); + cy.get('*[id^="metrics_"]').uncheck({ force: true }); + } + cy.getBySel('toastCloseButton').click(); cy.getBySel(CREATE_PACKAGE_POLICY_SAVE_BTN).click(); // sometimes agent is assigned to default policy, sometimes not cy.getBySel(CONFIRM_MODAL_BTN).click(); @@ -33,19 +42,15 @@ export function clickIfVisible(selector: string) { export const deleteIntegrations = async (integration: string) => { const ids: string[] = []; - cy.getBySel(INTEGRATION_NAME_LINK) - .each(($a) => { - const href = $a.attr('href') as string; - ids.push(href.substr(href.lastIndexOf('/') + 1)); - }) - .then(() => { - cy.request({ - url: `/api/fleet/package_policies/delete`, - headers: { 'kbn-xsrf': 'cypress' }, - body: `{ "packagePolicyIds": ${JSON.stringify(ids)} }`, - method: 'POST', - }); + cy.request('/api/fleet/package_policies').then((response: any) => { + response.body.items.forEach((policy: any) => ids.push(policy.id)); + cy.request({ + url: `/api/fleet/package_policies/delete`, + headers: { 'kbn-xsrf': 'cypress' }, + body: `{ "packagePolicyIds": ${JSON.stringify(ids)} }`, + method: 'POST', }); + }); }; export const installPackageWithVersion = (integration: string, version: string) => { diff --git a/x-pack/plugins/fleet/cypress/tsconfig.json b/x-pack/plugins/fleet/cypress/tsconfig.json index 1adb067fe682e..0d8d83e1feeb7 100644 --- a/x-pack/plugins/fleet/cypress/tsconfig.json +++ b/x-pack/plugins/fleet/cypress/tsconfig.json @@ -13,5 +13,6 @@ "node" ], "resolveJsonModule": true, + "target": "ES2019", }, } diff --git a/x-pack/plugins/fleet/public/applications/fleet/hooks/index.ts b/x-pack/plugins/fleet/public/applications/fleet/hooks/index.ts index 7cf16b1b7ae90..63438170c7faf 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/hooks/index.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/hooks/index.ts @@ -7,4 +7,4 @@ export * from '../../../hooks'; -export { useBreadcrumbs } from './use_breadcrumbs'; +export * from './use_breadcrumbs'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields.tsx new file mode 100644 index 0000000000000..d26dc83084a20 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields.tsx @@ -0,0 +1,318 @@ +/* + * 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, { useState } from 'react'; +import { + EuiDescribedFormGroup, + EuiFormRow, + EuiSpacer, + EuiComboBox, + EuiIconTip, + EuiCheckboxGroup, + EuiButton, + EuiLink, + EuiFieldNumber, + EuiFieldText, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; + +import { dataTypes } from '../../../../../../common'; +import type { NewAgentPolicy, AgentPolicy } from '../../../types'; +import { useStartServices } from '../../../hooks'; + +import { AgentPolicyPackageBadge } from '../../../components'; + +import { policyHasFleetServer } from '../../agents/services/has_fleet_server'; + +import { AgentPolicyDeleteProvider } from './agent_policy_delete_provider'; +import type { ValidationResults } from './agent_policy_validation'; + +interface Props { + agentPolicy: Partial; + updateAgentPolicy: (u: Partial) => void; + validation: ValidationResults; + isEditing?: boolean; + onDelete?: () => void; +} + +export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent = ({ + agentPolicy, + updateAgentPolicy, + validation, + isEditing = false, + onDelete = () => {}, +}) => { + const { docLinks } = useStartServices(); + const [touchedFields, setTouchedFields] = useState<{ [key: string]: boolean }>({}); + + // agent monitoring checkbox group can appear multiple times in the DOM, ids have to be unique to work correctly + const monitoringCheckboxIdSuffix = Date.now(); + + return ( + <> + + + + } + description={ + + } + > + + updateAgentPolicy({ description: e.target.value })} + isInvalid={Boolean(touchedFields.description && validation.description)} + onBlur={() => setTouchedFields({ ...touchedFields, description: true })} + placeholder={i18n.translate('xpack.fleet.agentPolicyForm.descriptionFieldPlaceholder', { + defaultMessage: 'Optional description', + })} + /> + + + + + + } + description={ + + {i18n.translate( + 'xpack.fleet.agentPolicyForm.nameSpaceFieldDescription.fleetUserGuideLabel', + { defaultMessage: 'Learn more' } + )} + + ), + }} + /> + } + > + + { + updateAgentPolicy({ namespace: value }); + }} + onChange={(selectedOptions) => { + updateAgentPolicy({ + namespace: (selectedOptions.length ? selectedOptions[0] : '') as string, + }); + }} + isInvalid={Boolean(touchedFields.namespace && validation.namespace)} + onBlur={() => setTouchedFields({ ...touchedFields, namespace: true })} + /> + + + + + + } + description={ + + ), + }} + /> + } + > + + {' '} + + + ), + }, + { + id: `${dataTypes.Metrics}_${monitoringCheckboxIdSuffix}`, + label: ( + <> + {' '} + + + ), + }, + ]} + idToSelectedMap={(agentPolicy.monitoring_enabled || []).reduce( + (acc: { [key: string]: boolean }, key) => { + acc[`${key}_${monitoringCheckboxIdSuffix}`] = true; + return acc; + }, + { logs: false, metrics: false } + )} + onChange={(longId) => { + const id = longId.split('_')[0]; + if (id !== dataTypes.Logs && id !== dataTypes.Metrics) { + return; + } + + const hasLogs = + agentPolicy.monitoring_enabled && agentPolicy.monitoring_enabled.indexOf(id) >= 0; + + const previousValues = agentPolicy.monitoring_enabled || []; + updateAgentPolicy({ + monitoring_enabled: hasLogs + ? previousValues.filter((type) => type !== id) + : [...previousValues, id], + }); + }} + /> + + + + + } + description={ + + } + > + + { + updateAgentPolicy({ + unenroll_timeout: e.target.value ? Number(e.target.value) : 0, + }); + }} + isInvalid={Boolean(touchedFields.unenroll_timeout && validation.unenroll_timeout)} + onBlur={() => setTouchedFields({ ...touchedFields, unenroll_timeout: true })} + /> + + + {isEditing && 'id' in agentPolicy && !agentPolicy.is_managed ? ( + + + + } + description={ + <> + + + + {(deleteAgentPolicyPrompt) => { + return ( + deleteAgentPolicyPrompt(agentPolicy.id!, onDelete)} + > + + + ); + }} + + + } + /> + ) : null} + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_create_inline.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_create_inline.tsx new file mode 100644 index 0000000000000..afab3980c1f56 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_create_inline.tsx @@ -0,0 +1,187 @@ +/* + * 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 } from 'react'; +import { + EuiAccordion, + EuiButton, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiForm, + EuiFormRow, + EuiLink, + EuiSpacer, + EuiText, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import styled from 'styled-components'; +import { i18n } from '@kbn/i18n'; + +import { dataTypes } from '../../../../../../common'; +import { agentPolicyFormValidation } from '../components'; + +import type { AgentPolicy, NewAgentPolicy } from '../../../types'; + +import { sendCreateAgentPolicy } from '../../../hooks'; + +import { AgentPolicyAdvancedOptionsContent } from './agent_policy_advanced_fields'; +import { AgentPolicyFormSystemMonitoringCheckbox } from './agent_policy_system_monitoring_field'; + +const StyledEuiAccordion = styled(EuiAccordion)` + .ingest-active-button { + color: ${(props) => props.theme.eui.euiColorPrimary}; + } +`; + +interface Props { + updateAgentPolicy: (u: AgentPolicy | null) => void; + isFleetServerPolicy?: boolean; + agentPolicyName: string; +} + +export const AgentPolicyCreateInlineForm: React.FunctionComponent = ({ + updateAgentPolicy, + isFleetServerPolicy, + agentPolicyName, +}) => { + const [touchedFields, setTouchedFields] = useState<{ [key: string]: boolean }>({}); + + const [withSysMonitoring, setWithSysMonitoring] = useState(true); + + const [isLoading, setIsLoading] = useState(false); + + const [newAgentPolicy, setNewAgentPolicy] = useState({ + name: agentPolicyName, + description: '', + namespace: 'default', + monitoring_enabled: Object.values(dataTypes), + has_fleet_server: isFleetServerPolicy, + }); + + const updateNewAgentPolicy = useCallback( + (updatedFields: Partial) => { + setNewAgentPolicy({ + ...newAgentPolicy, + ...updatedFields, + }); + }, + [setNewAgentPolicy, newAgentPolicy] + ); + + const validation = agentPolicyFormValidation(newAgentPolicy); + + const createAgentPolicy = useCallback(async () => { + try { + setIsLoading(true); + const resp = await sendCreateAgentPolicy(newAgentPolicy, { withSysMonitoring }); + if (resp.error) throw resp.error; + if (resp.data) { + updateAgentPolicy(resp.data.item); + } + } catch (e) { + updateAgentPolicy(null); + } finally { + setIsLoading(false); + } + }, [newAgentPolicy, withSysMonitoring, updateAgentPolicy]); + + return ( + + + {isFleetServerPolicy ? ( + + ) : ( + + + + ), + }} + /> + )} + + + + + + updateNewAgentPolicy({ name: e.target.value })} + isInvalid={Boolean(touchedFields.name && validation.name)} + onBlur={() => setTouchedFields({ ...touchedFields, name: true })} + placeholder={i18n.translate('xpack.fleet.agentPolicyForm.nameFieldPlaceholder', { + defaultMessage: 'Choose a name', + })} + /> + + + + createAgentPolicy()} + isLoading={isLoading} + data-test-subj={isFleetServerPolicy ? 'createFleetServerPolicyBtn' : 'createPolicyBtn'} + > + + + + + + setWithSysMonitoring(value)} + /> + + <> + + + } + buttonClassName="ingest-active-button" + > + + {}} + /> + + + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_delete_provider.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_delete_provider.tsx index 4041766113991..96bbc5b82565e 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_delete_provider.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_delete_provider.tsx @@ -15,13 +15,17 @@ import { sendDeleteAgentPolicy, useStartServices, useConfig, sendRequest } from interface Props { children: (deleteAgentPolicy: DeleteAgentPolicy) => React.ReactElement; + hasFleetServer: boolean; } export type DeleteAgentPolicy = (agentPolicy: string, onSuccess?: OnSuccessCallback) => void; type OnSuccessCallback = (agentPolicyDeleted: string) => void; -export const AgentPolicyDeleteProvider: React.FunctionComponent = ({ children }) => { +export const AgentPolicyDeleteProvider: React.FunctionComponent = ({ + children, + hasFleetServer, +}) => { const { notifications } = useStartServices(); const { agents: { enabled: isFleetEnabled }, @@ -165,6 +169,11 @@ export const AgentPolicyDeleteProvider: React.FunctionComponent = ({ chil }} /> + ) : hasFleetServer ? ( + ) : ( ; -} +import { AgentPolicyAdvancedOptionsContent } from './agent_policy_advanced_fields'; +import { AgentPolicyGeneralFields } from './agent_policy_general_fields'; +import { AgentPolicyFormSystemMonitoringCheckbox } from './agent_policy_system_monitoring_field'; +import type { ValidationResults } from './agent_policy_validation'; const StyledEuiAccordion = styled(EuiAccordion)` .ingest-active-button { @@ -44,37 +29,6 @@ const StyledEuiAccordion = styled(EuiAccordion)` } `; -export const agentPolicyFormValidation = ( - agentPolicy: Partial -): ValidationResults => { - const errors: ValidationResults = {}; - const namespaceValidation = isValidNamespace(agentPolicy.namespace || ''); - - if (!agentPolicy.name?.trim()) { - errors.name = [ - , - ]; - } - - if (!namespaceValidation.valid && namespaceValidation.error) { - errors.namespace = [namespaceValidation.error]; - } - - if (agentPolicy.unenroll_timeout && agentPolicy.unenroll_timeout < 0) { - errors.unenroll_timeout = [ - , - ]; - } - - return errors; -}; - interface Props { agentPolicy: Partial; updateAgentPolicy: (u: Partial) => void; @@ -94,38 +48,6 @@ export const AgentPolicyForm: React.FunctionComponent = ({ isEditing = false, onDelete = () => {}, }) => { - const { docLinks } = useStartServices(); - const [touchedFields, setTouchedFields] = useState<{ [key: string]: boolean }>({}); - const fields: Array<{ - name: 'name' | 'description' | 'namespace'; - label: JSX.Element; - placeholder: string; - }> = useMemo(() => { - return [ - { - name: 'name', - label: ( - - ), - placeholder: i18n.translate('xpack.fleet.agentPolicyForm.nameFieldPlaceholder', { - defaultMessage: 'Choose a name', - }), - }, - { - name: 'description', - label: ( - - ), - placeholder: i18n.translate('xpack.fleet.agentPolicyForm.descriptionFieldPlaceholder', { - defaultMessage: 'How will this policy be used?', - }), - }, - ]; - }, []); - const generalSettingsWrapper = (children: JSX.Element[]) => ( = ({ ); - const generalFields = fields.map(({ name, label, placeholder }) => { - return ( - - updateAgentPolicy({ [name]: e.target.value })} - isInvalid={Boolean(touchedFields[name] && validation[name])} - onBlur={() => setTouchedFields({ ...touchedFields, [name]: true })} - placeholder={placeholder} - /> - - ); - }); - - const advancedOptionsContent = ( - <> - - - - } - description={ - - {i18n.translate( - 'xpack.fleet.agentPolicyForm.nameSpaceFieldDescription.fleetUserGuideLabel', - { defaultMessage: 'Learn more' } - )} - - ), - }} - /> - } - > - - { - updateAgentPolicy({ namespace: value }); - }} - onChange={(selectedOptions) => { - updateAgentPolicy({ - namespace: (selectedOptions.length ? selectedOptions[0] : '') as string, - }); - }} - isInvalid={Boolean(touchedFields.namespace && validation.namespace)} - onBlur={() => setTouchedFields({ ...touchedFields, namespace: true })} - /> - - - - - - } - description={ - - } - > - - {' '} - - - ), - }, - { - id: dataTypes.Metrics, - label: ( - <> - {' '} - - - ), - }, - ]} - idToSelectedMap={(agentPolicy.monitoring_enabled || []).reduce( - (acc: { logs: boolean; metrics: boolean }, key) => { - acc[key] = true; - return acc; - }, - { logs: false, metrics: false } - )} - onChange={(id) => { - if (id !== dataTypes.Logs && id !== dataTypes.Metrics) { - return; - } - - const hasLogs = - agentPolicy.monitoring_enabled && agentPolicy.monitoring_enabled.indexOf(id) >= 0; - - const previousValues = agentPolicy.monitoring_enabled || []; - updateAgentPolicy({ - monitoring_enabled: hasLogs - ? previousValues.filter((type) => type !== id) - : [...previousValues, id], - }); - }} - /> - - - - - } - description={ - - } - > - - { - updateAgentPolicy({ - unenroll_timeout: e.target.value ? Number(e.target.value) : 0, - }); - }} - isInvalid={Boolean(touchedFields.unenroll_timeout && validation.unenroll_timeout)} - onBlur={() => setTouchedFields({ ...touchedFields, unenroll_timeout: true })} - /> - - - {isEditing && - 'id' in agentPolicy && - !agentPolicy.is_managed && - !agentPolicy.is_default && - !agentPolicy.is_default_fleet_server ? ( - - - - } - description={ - <> - - - - {(deleteAgentPolicyPrompt) => { - return ( - deleteAgentPolicyPrompt(agentPolicy.id!, onDelete)} - > - - - ); - }} - - {agentPolicy.is_default ? ( - <> - - - - - - ) : null} - - } - /> - ) : null} - - ); - return ( - {!isEditing ? generalFields : generalSettingsWrapper(generalFields)} {!isEditing ? ( - - } - > - - {' '} - - - } - checked={withSysMonitoring} - onChange={() => { - updateSysMonitoring(!withSysMonitoring); - }} - /> - + + ) : ( + generalSettingsWrapper([ + , + ]) + )} + {!isEditing ? ( + ) : null} {!isEditing ? ( <> @@ -459,11 +107,23 @@ export const AgentPolicyForm: React.FunctionComponent = ({ buttonClassName="ingest-active-button" > - {advancedOptionsContent} + ) : ( - advancedOptionsContent + )} ); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_general_fields.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_general_fields.tsx new file mode 100644 index 0000000000000..c71803e45aed5 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_general_fields.tsx @@ -0,0 +1,58 @@ +/* + * 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 { ReactElement } from 'react'; +import React, { useState } from 'react'; +import { EuiFieldText, EuiFormRow } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; + +import type { NewAgentPolicy, AgentPolicy } from '../../../types'; + +import type { ValidationResults } from './agent_policy_validation'; + +interface Props { + agentPolicy: Partial; + updateAgentPolicy: (u: Partial) => void; + validation: ValidationResults; + nameLabel?: ReactElement; +} + +export const AgentPolicyGeneralFields: React.FunctionComponent = ({ + agentPolicy, + updateAgentPolicy, + validation, + nameLabel, +}) => { + const [touchedFields, setTouchedFields] = useState<{ [key: string]: boolean }>({}); + + return ( + + ) + } + error={touchedFields.name && validation.name ? validation.name : null} + isInvalid={Boolean(touchedFields.name && validation.name)} + > + updateAgentPolicy({ name: e.target.value })} + isInvalid={Boolean(touchedFields.name && validation.name)} + onBlur={() => setTouchedFields({ ...touchedFields, name: true })} + placeholder={i18n.translate('xpack.fleet.agentPolicyForm.nameFieldPlaceholder', { + defaultMessage: 'Choose a name', + })} + /> + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_integration.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_integration.tsx new file mode 100644 index 0000000000000..07b4d59c9a258 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_integration.tsx @@ -0,0 +1,112 @@ +/* + * 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 { + EuiAccordion, + EuiDescribedFormGroup, + EuiForm, + EuiHorizontalRule, + EuiSpacer, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import styled from 'styled-components'; + +import type { NewAgentPolicy, AgentPolicy } from '../../../types'; + +import { AgentPolicyAdvancedOptionsContent } from './agent_policy_advanced_fields'; +import { AgentPolicyGeneralFields } from './agent_policy_general_fields'; +import { AgentPolicyFormSystemMonitoringCheckbox } from './agent_policy_system_monitoring_field'; +import type { ValidationResults } from './agent_policy_validation'; + +const StyledEuiAccordion = styled(EuiAccordion)` + .ingest-active-button { + color: ${(props) => props.theme.eui.euiColorPrimary}; + } +`; + +interface Props { + agentPolicy: Partial; + updateAgentPolicy: (u: Partial) => void; + withSysMonitoring: boolean; + updateSysMonitoring: (newValue: boolean) => void; + validation: ValidationResults; + isEditing?: boolean; + onDelete?: () => void; +} + +export const AgentPolicyIntegrationForm: React.FunctionComponent = ({ + agentPolicy, + updateAgentPolicy, + withSysMonitoring, + updateSysMonitoring, + validation, + isEditing = false, + onDelete = () => {}, +}) => { + return ( + + + + + } + description={ + + } + > + + } + /> + + + + <> + + + + } + buttonClassName="ingest-active-button" + data-test-subj="advancedOptionsBtn" + > + + + + + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_system_monitoring_field.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_system_monitoring_field.tsx new file mode 100644 index 0000000000000..d44ab502e8de0 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_system_monitoring_field.tsx @@ -0,0 +1,57 @@ +/* + * 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 { EuiFormRow, EuiIconTip, EuiCheckbox } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { AgentPolicyPackageBadge } from '../../../components'; + +interface Props { + withSysMonitoring: boolean; + updateSysMonitoring: (newValue: boolean) => void; +} + +export const AgentPolicyFormSystemMonitoringCheckbox: React.FunctionComponent = ({ + withSysMonitoring, + updateSysMonitoring, +}) => { + return ( + + + {' '} + , + }} + /> + } + position="right" + type="iInCircle" + color="subdued" + /> + + } + checked={withSysMonitoring} + onChange={() => { + updateSysMonitoring(!withSysMonitoring); + }} + data-test-subj="agentPolicyFormSystemMonitoringCheckbox" + /> + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.test.tsx new file mode 100644 index 0000000000000..961a0c7ca899d --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.test.tsx @@ -0,0 +1,42 @@ +/* + * 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 { agentPolicyFormValidation } from './agent_policy_validation'; +describe('Agent Policy form validation', () => { + it('should not return errors when agentPolicy is valid', () => { + const result = agentPolicyFormValidation({ + namespace: 'default', + name: 'policy', + }); + expect(result).toEqual({}); + }); + + it('should return error when agentPolicy has empty name', () => { + const result = agentPolicyFormValidation({ + namespace: 'default', + name: '', + }); + expect(result.name).toBeDefined(); + }); + + it('should return error when agentPolicy has empty namespace', () => { + const result = agentPolicyFormValidation({ + namespace: 'Default', + name: 'policy', + }); + expect(result.namespace).toBeDefined(); + }); + + it('should return error when agentPolicy has negative unenroll timeout', () => { + const result = agentPolicyFormValidation({ + namespace: 'Default', + name: 'policy', + unenroll_timeout: -1, + }); + expect(result.unenroll_timeout).toBeDefined(); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.tsx new file mode 100644 index 0000000000000..c75ce39fa7ff9 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.tsx @@ -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 React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { isValidNamespace } from '../../../services'; +import type { NewAgentPolicy, AgentPolicy } from '../../../types'; + +export interface ValidationResults { + [key: string]: Array; +} + +export const agentPolicyFormValidation = ( + agentPolicy: Partial +): ValidationResults => { + const errors: ValidationResults = {}; + const namespaceValidation = isValidNamespace(agentPolicy.namespace || ''); + + if (!agentPolicy.name?.trim()) { + errors.name = [ + , + ]; + } + + if (!namespaceValidation.valid && namespaceValidation.error) { + errors.namespace = [namespaceValidation.error]; + } + + if (agentPolicy.unenroll_timeout && agentPolicy.unenroll_timeout < 0) { + errors.unenroll_timeout = [ + , + ]; + } + + return errors; +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/index.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/index.ts index 439e474d416cb..f57714f7e76f5 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/index.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/index.ts @@ -8,9 +8,12 @@ // TODO: Does this need to be re-exported here? export { LinkedAgentCount } from '../../../components'; -export { AgentPolicyForm, agentPolicyFormValidation } from './agent_policy_form'; +export { AgentPolicyForm } from './agent_policy_form'; export { AgentPolicyCopyProvider } from './agent_policy_copy_provider'; export { AgentPolicyDeleteProvider } from './agent_policy_delete_provider'; export { AgentPolicyYamlFlyout } from './agent_policy_yaml_flyout'; export { ConfirmDeployAgentPolicyModal } from './confirm_deploy_modal'; export { AgentPolicyActionMenu } from './actions_menu'; +export { AgentPolicyIntegrationForm } from './agent_policy_integration'; +export { agentPolicyFormValidation } from './agent_policy_validation'; +export { AgentPolicyCreateInlineForm } from './agent_policy_create_inline'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/package_policy_input_panel.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/package_policy_input_panel.tsx index e8366a38c0c60..4883972c455d5 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/package_policy_input_panel.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/package_policy_input_panel.tsx @@ -83,7 +83,7 @@ export const PackagePolicyInputPanel: React.FunctionComponent<{ ); // Errors state - const errorCount = countValidationErrors(inputValidationResults); + const errorCount = inputValidationResults && countValidationErrors(inputValidationResults); const hasErrors = forceShowErrors && errorCount; const hasInputStreams = useMemo( diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/package_policy_input_stream.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/package_policy_input_stream.tsx index b8bc0fdb03ad5..edc7d6f02bc0a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/package_policy_input_stream.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/package_policy_input_stream.tsx @@ -106,6 +106,7 @@ export const PackagePolicyInputStreamConfig: React.FunctionComponent<{ {requiredVars.map((varDef) => { + if (!packagePolicyInputStream.vars) return null; const { name: varName, type: varType } = varDef; const varConfigEntry = packagePolicyInputStream.vars?.[varName]; const value = varConfigEntry?.value; @@ -166,6 +167,7 @@ export const PackagePolicyInputStreamConfig: React.FunctionComponent<{ {isShowingAdvanced ? advancedVars.map((varDef) => { + if (!packagePolicyInputStream.vars) return null; const { name: varName, type: varType } = varDef; const value = packagePolicyInputStream.vars?.[varName]?.value; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx index 732231e178e8d..743ff40ecf5e6 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx @@ -24,9 +24,10 @@ import { import type { EuiStepProps } from '@elastic/eui/src/components/steps/step'; import { safeLoad } from 'js-yaml'; -import { splitPkgKey } from '../../../../../../common'; +import { dataTypes, splitPkgKey } from '../../../../../../common'; import type { AgentPolicy, + NewAgentPolicy, NewPackagePolicy, PackagePolicy, CreatePackagePolicyRouteState, @@ -40,9 +41,10 @@ import { useConfig, sendGetAgentStatus, useGetPackageInfoByKey, + sendCreateAgentPolicy, } from '../../../hooks'; import { Loading, Error } from '../../../components'; -import { ConfirmDeployAgentPolicyModal } from '../components'; +import { agentPolicyFormValidation, ConfirmDeployAgentPolicyModal } from '../components'; import { useIntraAppState, useUIExtension } from '../../../hooks'; import { ExtensionWrapper } from '../../../components'; import type { PackagePolicyEditExtensionComponentProps } from '../../../types'; @@ -53,9 +55,9 @@ import type { EditPackagePolicyFrom, PackagePolicyFormState } from './types'; import type { PackagePolicyValidationResults } from './services'; import { validatePackagePolicy, validationHasErrors } from './services'; import { appendOnSaveQueryParamsToPath } from './utils'; -import { StepSelectAgentPolicy } from './step_select_agent_policy'; import { StepConfigurePackagePolicy } from './step_configure_package'; import { StepDefinePackagePolicy } from './step_define_package_policy'; +import { SelectedPolicyTab, StepSelectHosts } from './step_select_hosts'; const StepsWithLessPadding = styled(EuiSteps)` .euiStep__content { @@ -116,6 +118,16 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { // Agent policy state const [agentPolicy, setAgentPolicy] = useState(); + const [newAgentPolicy, setNewAgentPolicy] = useState({ + name: 'Agent policy 1', + description: '', + namespace: 'default', + monitoring_enabled: Object.values(dataTypes), + }); + + const [withSysMonitoring, setWithSysMonitoring] = useState(true); + const validation = agentPolicyFormValidation(newAgentPolicy); + // only used to store the resulting package policy once saved const [savedPackagePolicy, setSavedPackagePolicy] = useState(); @@ -135,19 +147,21 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { }, [agentPolicyId, isFleetEnabled]); const [agentCount, setAgentCount] = useState(0); + const [selectedPolicyTab, setSelectedPolicyTab] = useState( + queryParamsPolicyId ? SelectedPolicyTab.EXISTING : SelectedPolicyTab.NEW + ); + // New package policy state const [packagePolicy, setPackagePolicy] = useState({ name: '', description: '', - namespace: '', + namespace: 'default', policy_id: '', enabled: true, output_id: '', // TODO: Blank for now as we only support default output inputs: [], }); - const [wasNewAgentPolicyCreated, setWasNewAgentPolicyCreated] = useState(false); - // Validation state const [validationResults, setValidationResults] = useState(); const [hasAgentPolicyError, setHasAgentPolicyError] = useState(false); @@ -187,6 +201,39 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { [packageInfo, setAgentPolicy] ); + const setPolicyValidation = ( + selectedTab: SelectedPolicyTab, + updatedAgentPolicy: NewAgentPolicy + ) => { + if (selectedTab === SelectedPolicyTab.NEW) { + if (!updatedAgentPolicy.name || !updatedAgentPolicy.namespace) { + setHasAgentPolicyError(true); + } else { + setHasAgentPolicyError(false); + } + } + }; + + const updateNewAgentPolicy = useCallback( + (updatedFields: Partial) => { + const updatedAgentPolicy = { + ...newAgentPolicy, + ...updatedFields, + }; + setNewAgentPolicy(updatedAgentPolicy); + setPolicyValidation(selectedPolicyTab, updatedAgentPolicy); + }, + [setNewAgentPolicy, newAgentPolicy, selectedPolicyTab] + ); + + const updateSelectedPolicy = useCallback( + (policy) => { + setSelectedPolicyTab(policy); + setPolicyValidation(policy, newAgentPolicy); + }, + [setSelectedPolicyTab, newAgentPolicy] + ); + const hasErrors = validationResults ? validationHasErrors(validationResults) : false; // Update package policy validation @@ -225,13 +272,17 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { ? validationHasErrors(newValidationResults) : false; const hasAgentPolicy = newPackagePolicy.policy_id && newPackagePolicy.policy_id !== ''; - if (hasPackage && hasAgentPolicy && !hasValidationErrors) { + if ( + hasPackage && + (hasAgentPolicy || selectedPolicyTab === SelectedPolicyTab.NEW) && + !hasValidationErrors + ) { setFormState('VALID'); } else { setFormState('INVALID'); } }, - [packagePolicy, updatePackagePolicyValidation] + [packagePolicy, updatePackagePolicyValidation, selectedPolicyTab] ); const handleExtensionViewOnChange = useCallback< @@ -272,18 +323,17 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { ); // Save package policy - const savePackagePolicy = useCallback(async () => { - setFormState('LOADING'); - const result = await sendCreatePackagePolicy(packagePolicy); - setFormState(agentCount ? 'SUBMITTED' : 'SUBMITTED_NO_AGENTS'); - return result; - }, [packagePolicy, agentCount]); + const savePackagePolicy = useCallback( + async (pkgPolicy: NewPackagePolicy) => { + setFormState('LOADING'); + const result = await sendCreatePackagePolicy(pkgPolicy); + setFormState(agentCount ? 'SUBMITTED' : 'SUBMITTED_NO_AGENTS'); + return result; + }, + [agentCount] + ); const doOnSaveNavigation = useRef(true); - const handleInlineAgentPolicyCreate = useCallback(() => { - setWasNewAgentPolicyCreated(true); - }, []); - // Detect if user left page useEffect(() => { return () => { @@ -310,9 +360,9 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { if (options?.path) { const pathWithQueryString = appendOnSaveQueryParamsToPath({ - // In cases where we created a new agent policy inline, we need to override the initial `path` - // value and navigate to the newly-created agent policy instead - path: wasNewAgentPolicyCreated ? packagePolicyPath : options.path, + // In cases where we want to navigate back to a new/existing policy, we need to override the initial `path` + // value and navigate to the actual agent policy instead + path: queryParamsPolicyId ? packagePolicyPath : options.path, policy, mappingOptions: routeState.onSaveQueryParams, paramsToApply, @@ -325,9 +375,26 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { history.push(packagePolicyPath); } }, - [packagePolicy.policy_id, getPath, navigateToApp, history, routeState, wasNewAgentPolicyCreated] + [packagePolicy.policy_id, getPath, navigateToApp, history, routeState, queryParamsPolicyId] ); + const createAgentPolicy = useCallback(async (): Promise => { + let policyId; + setFormState('LOADING'); + const resp = await sendCreateAgentPolicy(newAgentPolicy, { withSysMonitoring }); + if (resp.error) { + setFormState('VALID'); + throw resp.error; + } + if (resp.data) { + policyId = resp.data.item.id; + setAgentPolicy(resp.data.item); + + updatePackagePolicy({ policy_id: policyId }); + } + return policyId; + }, [newAgentPolicy, updatePackagePolicy, withSysMonitoring]); + const onSubmit = useCallback(async () => { if (formState === 'VALID' && hasErrors) { setFormState('INVALID'); @@ -337,7 +404,26 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { setFormState('CONFIRM'); return; } - const { error, data } = await savePackagePolicy(); + let policyId; + if (selectedPolicyTab === SelectedPolicyTab.NEW) { + try { + policyId = await createAgentPolicy(); + } catch (e) { + notifications.toasts.addError(e, { + title: i18n.translate('xpack.fleet.createAgentPolicy.errorNotificationTitle', { + defaultMessage: 'Unable to create agent policy', + }), + }); + return; + } + } + + setFormState('LOADING'); + // passing pkgPolicy with policy_id here as setPackagePolicy doesn't propagate immediately + const { error, data } = await savePackagePolicy({ + ...packagePolicy, + policy_id: policyId ?? packagePolicy.policy_id, + }); if (!error) { setSavedPackagePolicy(data!.item); @@ -379,7 +465,9 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { onSaveNavigate, agentPolicy, notifications.toasts, - packagePolicy.name, + packagePolicy, + selectedPolicyTab, + createAgentPolicy, ]); const integrationInfo = useMemo( @@ -406,21 +494,30 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { const stepSelectAgentPolicy = useMemo( () => ( - setWithSysMonitoring(newValue)} + validation={validation} + packageInfo={packageInfo} setHasAgentPolicyError={setHasAgentPolicyError} - onNewAgentPolicyCreate={handleInlineAgentPolicyCreate} + updateSelectedTab={updateSelectedPolicy} + selectedAgentPolicyId={queryParamsPolicyId} /> ), [ packageInfo, - queryParamsPolicyId, agentPolicy, updateAgentPolicy, - handleInlineAgentPolicyCreate, + newAgentPolicy, + updateNewAgentPolicy, + validation, + withSysMonitoring, + updateSelectedPolicy, + queryParamsPolicyId, ] ); @@ -491,7 +588,7 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { }, { title: i18n.translate('xpack.fleet.createPackagePolicy.stepSelectAgentPolicyTitle', { - defaultMessage: 'Apply to agent policy', + defaultMessage: 'Where to add this integration?', }), children: stepSelectAgentPolicy, }, @@ -544,7 +641,7 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { - {agentPolicy && packageInfo && formState === 'INVALID' ? ( + {packageInfo && (formState === 'INVALID' || hasAgentPolicyError) ? ( { { + return { + ...jest.requireActual('../../../hooks'), + useGetAgentPolicies: jest.fn(), + useFleetStatus: jest.fn().mockReturnValue({ isReady: true } as any), + sendGetFleetStatus: jest + .fn() + .mockResolvedValue({ data: { isReady: true, missing_requirements: [] } }), + }; +}); + +const useGetAgentPoliciesMock = useGetAgentPolicies as jest.MockedFunction< + typeof useGetAgentPolicies +>; + +describe('step select agent policy', () => { + let testRenderer: TestRenderer; + let renderResult: ReturnType; + const mockSetHasAgentPolicyError = jest.fn(); + const render = () => + (renderResult = testRenderer.render( + + )); + + beforeEach(() => { + testRenderer = createFleetTestRendererMock(); + }); + + test('should not select agent policy by default if multiple exists', async () => { + useGetAgentPoliciesMock.mockReturnValueOnce({ + data: { + items: [ + { id: 'policy-1', name: 'Policy 1' }, + { id: 'policy-2', name: 'Policy 2' }, + ], + }, + error: undefined, + isLoading: false, + resendRequest: jest.fn(), + } as any); + + render(); + + await act(async () => { + const select = renderResult.container.querySelector('[data-test-subj="agentPolicySelect"]'); + expect((select as any)?.value).toEqual(''); + + expect(renderResult.getAllByRole('option').length).toBe(2); + expect(renderResult.getByText('An agent policy is required.')).toBeVisible(); + }); + }); + + test('should select agent policy by default if one exists', async () => { + useGetAgentPoliciesMock.mockReturnValueOnce({ + data: { items: [{ id: 'policy-1', name: 'Policy 1' }] }, + error: undefined, + isLoading: false, + resendRequest: jest.fn(), + } as any); + + render(); + + await act(async () => { + const select = renderResult.container.querySelector('[data-test-subj="agentPolicySelect"]'); + expect((select as any)?.value).toEqual('policy-1'); + }); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_agent_policy.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_agent_policy.tsx index 692752e259389..8558fbecbde0f 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_agent_policy.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_agent_policy.tsx @@ -9,29 +9,21 @@ import React, { useEffect, useState, useMemo, useCallback } from 'react'; import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { EuiComboBoxOptionOption } from '@elastic/eui'; +import type { EuiSelectOption } from '@elastic/eui'; +import { EuiSelect } from '@elastic/eui'; import { EuiFlexGroup, EuiFlexItem, - EuiComboBox, - EuiPortal, EuiFormRow, EuiDescribedFormGroup, EuiTitle, EuiText, - EuiLink, } from '@elastic/eui'; import { Error } from '../../../components'; import type { AgentPolicy, PackageInfo, GetAgentPoliciesResponseItem } from '../../../types'; import { isPackageLimited, doesAgentPolicyAlreadyIncludePackage } from '../../../services'; -import { - useGetAgentPolicies, - sendGetOneAgentPolicy, - useAuthz, - useFleetStatus, -} from '../../../hooks'; -import { CreateAgentPolicyFlyout } from '../list_page/components'; +import { useGetAgentPolicies, sendGetOneAgentPolicy, useFleetStatus } from '../../../hooks'; const AgentPolicyFormRow = styled(EuiFormRow)` .euiFormRow__label { @@ -41,38 +33,26 @@ const AgentPolicyFormRow = styled(EuiFormRow)` export const StepSelectAgentPolicy: React.FunctionComponent<{ packageInfo?: PackageInfo; - defaultAgentPolicyId?: string; agentPolicy: AgentPolicy | undefined; updateAgentPolicy: (agentPolicy: AgentPolicy | undefined) => void; setHasAgentPolicyError: (hasError: boolean) => void; - onNewAgentPolicyCreate: () => void; + selectedAgentPolicyId?: string; }> = ({ packageInfo, agentPolicy, updateAgentPolicy, - defaultAgentPolicyId, setHasAgentPolicyError, - onNewAgentPolicyCreate, + selectedAgentPolicyId, }) => { const { isReady: isFleetReady } = useFleetStatus(); - // Selected agent policy state - const [selectedPolicyId, setSelectedPolicyId] = useState( - agentPolicy?.id ?? defaultAgentPolicyId - ); const [selectedAgentPolicyError, setSelectedAgentPolicyError] = useState(); - // Create new agent policy flyout state - const hasFleetAllPrivileges = useAuthz().fleet.all; - const [isCreateAgentPolicyFlyoutOpen, setIsCreateAgentPolicyFlyoutOpen] = - useState(false); - // Fetch agent policies info const { data: agentPoliciesData, error: agentPoliciesError, isLoading: isAgentPoliciesLoading, - resendRequest: refreshAgentPolicies, } = useGetAgentPolicies({ page: 1, perPage: 1000, @@ -92,6 +72,12 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{ }, {}); }, [agentPolicies]); + // Selected agent policy state + const [selectedPolicyId, setSelectedPolicyId] = useState( + agentPolicy?.id ?? + (selectedAgentPolicyId || (agentPolicies.length === 1 ? agentPolicies[0].id : undefined)) + ); + const doesAgentPolicyHaveLimitedPackage = useCallback( (policy: AgentPolicy, pkgInfo: PackageInfo) => { return policy @@ -123,12 +109,12 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{ } }, [selectedPolicyId, agentPolicy, updateAgentPolicy]); - const agentPolicyOptions: Array> = useMemo( + const agentPolicyOptions: EuiSelectOption[] = useMemo( () => packageInfo ? agentPolicies.map((agentConf) => { return { - label: agentConf.name, + text: agentConf.name, value: agentConf.id, disabled: doesAgentPolicyHaveLimitedPackage(agentConf, packageInfo), 'data-test-subj': 'agentPolicyItem', @@ -138,49 +124,22 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{ [agentPolicies, doesAgentPolicyHaveLimitedPackage, packageInfo] ); - const selectedAgentPolicyOption = useMemo( - () => agentPolicyOptions.find((option) => option.value === selectedPolicyId), - [agentPolicyOptions, selectedPolicyId] - ); - // Try to select default agent policy useEffect(() => { if (!selectedPolicyId && agentPolicies.length && agentPolicyOptions.length) { - const firstEnabledOption = agentPolicyOptions.find((option) => !option.disabled); - const defaultAgentPolicy = agentPolicies.find((policy) => policy.is_default); - if (defaultAgentPolicy) { - const defaultAgentPolicyOption = agentPolicyOptions.find( - (option) => option.value === defaultAgentPolicy.id - ); - if (defaultAgentPolicyOption && !defaultAgentPolicyOption.disabled) { - setSelectedPolicyId(defaultAgentPolicy.id); - } else { - if (firstEnabledOption) { - setSelectedPolicyId(firstEnabledOption.value); - } - } - } else if (firstEnabledOption) { - setSelectedPolicyId(firstEnabledOption.value); + const enabledOptions = agentPolicyOptions.filter((option) => !option.disabled); + if (enabledOptions.length === 1) { + setSelectedPolicyId(enabledOptions[0].value as string | undefined); } } }, [agentPolicies, agentPolicyOptions, selectedPolicyId]); // Bubble up any issues with agent policy selection useEffect(() => { - if ( - selectedPolicyId && - !selectedAgentPolicyError && - selectedAgentPolicyOption && - !selectedAgentPolicyOption.disabled - ) { + if (selectedPolicyId && !selectedAgentPolicyError) { setHasAgentPolicyError(false); } else setHasAgentPolicyError(true); - }, [ - selectedAgentPolicyError, - selectedAgentPolicyOption, - selectedPolicyId, - setHasAgentPolicyError, - ]); + }, [selectedAgentPolicyError, selectedPolicyId, setHasAgentPolicyError]); // Display agent policies list error if there is one if (agentPoliciesError) { @@ -199,21 +158,6 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{ return ( <> - {isCreateAgentPolicyFlyoutOpen ? ( - - { - setIsCreateAgentPolicyFlyoutOpen(false); - if (newAgentPolicy) { - onNewAgentPolicyCreate(); - refreshAgentPolicies(); - setSelectedPolicyId(newAgentPolicy.id); - } - }} - ownFocus={true} - /> - - ) : null} - -
- setIsCreateAgentPolicyFlyoutOpen(true)} - > - - -
-
} helpText={ @@ -296,29 +227,21 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{ ) } > - 1} + fullWidth isLoading={isAgentPoliciesLoading || !packageInfo} options={agentPolicyOptions} - selectedOptions={selectedAgentPolicyOption ? [selectedAgentPolicyOption] : []} - onChange={(options) => { - const selectedOption = options[0] || undefined; - if (selectedOption) { - if (selectedOption.value !== selectedPolicyId) { - setSelectedPolicyId(selectedOption.value); - } - } else { - setSelectedPolicyId(undefined); - } - }} + value={selectedPolicyId || undefined} + onChange={(e) => setSelectedPolicyId(e.target.value)} + data-test-subj="agentPolicySelect" + aria-label="Select Agent Policy" />
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_hosts.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_hosts.tsx new file mode 100644 index 0000000000000..5e454a51ea3a8 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_hosts.tsx @@ -0,0 +1,133 @@ +/* + * 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, { useEffect, useMemo } from 'react'; +import type { EuiTabbedContentTab } from '@elastic/eui'; +import { EuiTabbedContent } from '@elastic/eui'; +import styled from 'styled-components'; + +import { useGetAgentPolicies } from '../../../hooks'; +import type { AgentPolicy, NewAgentPolicy, PackageInfo } from '../../../types'; +import { AgentPolicyIntegrationForm } from '../components'; +import type { ValidationResults } from '../components/agent_policy_validation'; + +import { incrementPolicyName } from '../../../services'; + +import { StepSelectAgentPolicy } from './step_select_agent_policy'; + +export enum SelectedPolicyTab { + NEW = 'new', + EXISTING = 'existing', +} + +const StyledEuiTabbedContent = styled(EuiTabbedContent)` + [role='tabpanel'] { + padding-top: ${(props) => props.theme.eui.paddingSizes.m}; + } +`; + +interface Props { + agentPolicy: AgentPolicy | undefined; + updateAgentPolicy: (u: AgentPolicy | undefined) => void; + newAgentPolicy: Partial; + updateNewAgentPolicy: (u: Partial) => void; + withSysMonitoring: boolean; + updateSysMonitoring: (newValue: boolean) => void; + validation: ValidationResults; + packageInfo?: PackageInfo; + setHasAgentPolicyError: (hasError: boolean) => void; + updateSelectedTab: (tab: SelectedPolicyTab) => void; + selectedAgentPolicyId?: string; +} + +export const StepSelectHosts: React.FunctionComponent = ({ + agentPolicy, + updateAgentPolicy, + newAgentPolicy, + updateNewAgentPolicy, + withSysMonitoring, + updateSysMonitoring, + validation, + packageInfo, + setHasAgentPolicyError, + updateSelectedTab, + selectedAgentPolicyId, +}) => { + let agentPolicies: AgentPolicy[] = []; + const { data: agentPoliciesData, error: err } = useGetAgentPolicies({ + page: 1, + perPage: 1000, + sortField: 'name', + sortOrder: 'asc', + full: true, + }); + if (err) { + // eslint-disable-next-line no-console + console.debug('Could not retrieve agent policies'); + } + agentPolicies = useMemo( + () => agentPoliciesData?.items.filter((policy) => !policy.is_managed) || [], + [agentPoliciesData?.items] + ); + + useEffect(() => { + if (agentPolicies.length > 0) { + updateNewAgentPolicy({ + ...newAgentPolicy, + name: incrementPolicyName(agentPolicies), + }); + } + }, [agentPolicies.length]); // eslint-disable-line react-hooks/exhaustive-deps + + const tabs = [ + { + id: SelectedPolicyTab.NEW, + name: 'New hosts', + content: ( + + ), + }, + { + id: SelectedPolicyTab.EXISTING, + name: 'Existing hosts', + content: ( + + ), + }, + ]; + + const handleOnTabClick = (tab: EuiTabbedContentTab) => + updateSelectedTab(tab.id as SelectedPolicyTab); + + return agentPolicies.length > 0 ? ( + + ) : ( + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/no_package_policies.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/no_package_policies.tsx index 40b7428da0110..987a9e1610c5f 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/no_package_policies.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/no_package_policies.tsx @@ -43,6 +43,7 @@ export const NoPackagePolicies = memo<{ policyId: string }>(({ policyId }) => { state: { forAgentPolicyId: policyId }, }) } + data-test-subj="addPackagePolicyButton" > = ({ state: { forAgentPolicyId: agentPolicy.id }, }); }} + data-test-subj="addPackagePolicyButton" > = ({ name: '', description: '', namespace: 'default', - is_default: undefined, monitoring_enabled: Object.values(dataTypes), }); const [isLoading, setIsLoading] = useState(false); @@ -147,6 +146,7 @@ export const CreateAgentPolicyFlyout: React.FunctionComponent = ({ ); } }} + data-test-subj="createAgentPolicyFlyoutBtn" > - typeof ap !== 'string' && ap.package?.name === FLEET_SERVER_PACKAGE - ); + const hasFleetServer = agentPolicy && policyHasFleetServer(agentPolicy); const onClose = useMemo(() => { if (onCancelReassign) { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx index 377ded6edd271..ed4f435f284b3 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx @@ -380,10 +380,10 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { // Fleet server unhealthy status const { isUnhealthy: isFleetServerUnhealthy } = useFleetServerUnhealthy(); const onClickAddFleetServer = useCallback(() => { - const defaultPolicy = agentPolicies.find((policy) => policy.is_default_fleet_server); - if (defaultPolicy) { - setEnrollmentFlyoutState({ isOpen: true, selectedPolicyId: defaultPolicy.id }); - } + setEnrollmentFlyoutState({ + isOpen: true, + selectedPolicyId: agentPolicies.length > 0 ? agentPolicies[0].id : undefined, + }); }, [agentPolicies]); const columns = [ diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx index 0f8c719c2a337..c3f532da862ed 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx @@ -17,7 +17,6 @@ import { EuiCode, EuiCodeBlock, EuiCallOut, - EuiSelect, EuiRadioGroup, EuiFieldText, EuiForm, @@ -29,7 +28,7 @@ import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { DownloadStep } from '../../../../components'; +import { DownloadStep, SelectCreateAgentPolicy } from '../../../../components'; import { useStartServices, useDefaultOutput, @@ -44,10 +43,11 @@ import { useLink, } from '../../../../hooks'; import type { PLATFORM_TYPE } from '../../../../hooks'; -import type { PackagePolicy } from '../../../../types'; -import { FLEET_SERVER_PACKAGE } from '../../../../constants'; +import type { AgentPolicy } from '../../../../types'; import { FleetServerOnPremRequiredCallout } from '../../components'; +import { policyHasFleetServer } from '../../services/has_fleet_server'; + import { getInstallCommandForPlatform } from './install_command_utils'; const URL_REGEX = /^(https?):\/\/[^\s$.?#].[^\s]*$/gm; @@ -110,6 +110,7 @@ export const ServiceTokenStep = ({ onClick={() => { getServiceToken(); }} + data-test-subj="fleetServerGenerateServiceTokenBtn" > { - const { output, refresh: refreshOutputs } = useDefaultOutput(); + const { output } = useDefaultOutput(); const { notifications } = useStartServices(); const [serviceToken, setServiceToken] = useState(); const [isLoadingServiceToken, setIsLoadingServiceToken] = useState(false); @@ -289,10 +290,6 @@ export const useFleetServerInstructions = (policyId?: string) => { setIsLoadingServiceToken(false); }, [notifications.toasts]); - const refresh = useCallback(() => { - return Promise.all([refreshOutputs(), refreshSettings()]); - }, [refreshOutputs, refreshSettings]); - const addFleetServerHost = useCallback( async (host: string) => { const res = await sendPutSettings({ @@ -317,7 +314,6 @@ export const useFleetServerInstructions = (policyId?: string) => { installCommand, platform, setPlatform, - refresh, }; }; @@ -328,71 +324,48 @@ const AgentPolicySelectionStep = ({ policyId?: string; setPolicyId: (v: string) => void; }): EuiStepProps => { - const { data } = useGetAgentPolicies({ full: true }); + const { data, resendRequest: refreshAgentPolicies } = useGetAgentPolicies({ full: true }); const agentPolicies = useMemo( - () => - data - ? data.items.filter((item) => { - return item.package_policies.some( - (p: string | PackagePolicy) => - (p as PackagePolicy).package?.name === FLEET_SERVER_PACKAGE - ); - return false; - }) - : [], + () => (data ? data.items.filter((item) => policyHasFleetServer(item)) : []), [data] ); - const options = useMemo(() => { - return agentPolicies.map((policy) => ({ text: policy.name, value: policy.id })); - }, [agentPolicies]); - useEffect(() => { // Select default value if (agentPolicies.length && !policyId) { - const defaultPolicy = - agentPolicies.find((p) => p.is_default_fleet_server) || agentPolicies[0]; - setPolicyId(defaultPolicy.id); + setPolicyId(agentPolicies[0].id); } - }, [options, agentPolicies, policyId, setPolicyId]); + }, [agentPolicies, policyId, setPolicyId]); const onChangeCallback = useCallback( - (e: React.ChangeEvent) => { - setPolicyId(e.target.value); + (key: string | undefined, policy?: AgentPolicy) => { + if (policy) { + refreshAgentPolicies(); + } + setPolicyId(key!); }, - [setPolicyId] + [setPolicyId, refreshAgentPolicies] ); return { - title: i18n.translate('xpack.fleet.fleetServerSetup.stepSelectAgentPolicyTitle', { - defaultMessage: 'Select an Agent policy', - }), + title: + agentPolicies.length === 0 + ? i18n.translate('xpack.fleet.fleetServerSetup.stepCreateAgentPolicyTitle', { + defaultMessage: 'Create an agent policy to host Fleet Server', + }) + : i18n.translate('xpack.fleet.fleetServerSetup.stepSelectAgentPolicyTitle', { + defaultMessage: 'Select an agent policy to host Fleet Server', + }), status: undefined, children: ( <> - - - - - - - - } - options={options} - value={policyId} - onChange={onChangeCallback} - aria-label={i18n.translate('xpack.fleet.fleetServerSetup.agentPolicySelectAraiLabel', { - defaultMessage: 'Agent policy', - })} + ), @@ -501,11 +474,16 @@ export const AddFleetServerHostStepContent = ({ /> } + data-test-subj="fleetServerHostInput" /> {error && {error}} - + { const onChangeCallback = useCallback( (v: string) => { - if (v === 'production' || v === 'quickstart') { - setDeploymentMode(v); + const value = v.split('_')[0]; + if (value === 'production' || value === 'quickstart') { + setDeploymentMode(value); } }, [setDeploymentMode] ); + // radio id has to be unique so that the component works even if appears twice in DOM (Agents tab, Add agent flyout) + const radioSuffix = useMemo(() => Date.now(), []); + return ( <> @@ -598,7 +580,7 @@ const DeploymentModeStepContent = ({ ); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_policy_created_callout.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_policy_created_callout.tsx new file mode 100644 index 0000000000000..ba3af7716d985 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_policy_created_callout.tsx @@ -0,0 +1,52 @@ +/* + * 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 { EuiSpacer, EuiCallOut } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +export enum CREATE_STATUS { + INITIAL = 'initial', + CREATED = 'created', + FAILED = 'failed', +} + +interface Props { + createStatus: CREATE_STATUS; +} + +export const AgentPolicyCreatedCallOut: React.FunctionComponent = ({ createStatus }) => { + return ( + <> + + {createStatus === CREATE_STATUS.CREATED ? ( + + } + color="success" + iconType="check" + /> + ) : ( + + } + color="danger" + iconType="cross" + /> + )} + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/fleet_server_callouts/fleet_server_on_prem_unhealthy_callout.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/fleet_server_callouts/fleet_server_on_prem_unhealthy_callout.tsx index e7ba51ebf5247..d74433ca48a5b 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/fleet_server_callouts/fleet_server_on_prem_unhealthy_callout.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/fleet_server_callouts/fleet_server_on_prem_unhealthy_callout.tsx @@ -44,7 +44,12 @@ export const FleetServerOnPremUnhealthyCallout: React.FunctionComponent< }} /> - + { const agentPoliciesRequest = useGetAgentPolicies({ page: 1, perPage: 1000, + full: true, }); const agentPolicies = useMemo( @@ -110,7 +111,12 @@ export const AgentsApp: React.FunctionComponent = () => { )} - setIsEnrollmentFlyoutOpen(true)}> + setIsEnrollmentFlyoutOpen(true)} + data-test-subj="addAgentBtnTop" + > diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/services/has_fleet_server.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/services/has_fleet_server.ts new file mode 100644 index 0000000000000..c10049303234c --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/services/has_fleet_server.ts @@ -0,0 +1,16 @@ +/* + * 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 { AgentPolicy, PackagePolicy } from '../../../types'; +import { FLEET_SERVER_PACKAGE } from '../../../constants'; + +export function policyHasFleetServer(agentPolicy: AgentPolicy) { + return agentPolicy.package_policies?.some( + (ap: string | PackagePolicy) => + typeof ap !== 'string' && ap.package?.name === FLEET_SERVER_PACKAGE + ); +} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx index 62b22d0bdffc6..6190d2b13c8fe 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx @@ -236,6 +236,7 @@ export const EditOutputFlyout: React.FunctionComponent = isLoading={form.isLoading} isDisabled={form.isDisabled} onClick={form.submit} + data-test-subj="saveApplySettingsBtn" > = ({ title={i18n.translate('xpack.fleet.settings.outputSection.editButtonTitle', { defaultMessage: 'Edit', })} + data-test-subj="editOutputBtn" /> diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/settings_section.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/settings_section.tsx index 8129943e0e6c0..13d3657276335 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/settings_section.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/settings_section.tsx @@ -64,7 +64,11 @@ export const SettingsSection: React.FunctionComponent = ({ - + = ({ /> ) : isLoadingEnrollmentApiKeys ? ( - ) : ( + ) : agentPolicyId ? ( - )} + ) : null} )} diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts index 5c292187982dc..79cfb7ff2b2d1 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts @@ -12,6 +12,7 @@ jest.mock('../../hooks/use_request', () => { useGetSettings: jest.fn(), sendGetFleetStatus: jest.fn(), sendGetOneAgentPolicy: jest.fn(), + useGetAgents: jest.fn(), }; }); @@ -62,3 +63,7 @@ jest.mock('@elastic/eui', () => { EuiSteps: 'eui-steps', }; }); + +jest.mock('../../applications/fleet/sections/agents/services/has_fleet_server', () => { + return { policyHasFleetServer: jest.fn().mockReturnValue(true) }; +}); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx index 18296134ee1a7..0e6138aea7cb4 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx @@ -16,7 +16,12 @@ import { coreMock } from 'src/core/public/mocks'; import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; import type { AgentPolicy } from '../../../common'; -import { useGetSettings, sendGetFleetStatus, sendGetOneAgentPolicy } from '../../hooks/use_request'; +import { + useGetSettings, + sendGetFleetStatus, + sendGetOneAgentPolicy, + useGetAgents, +} from '../../hooks/use_request'; import { FleetStatusProvider, ConfigContext } from '../../hooks'; import { useFleetServerInstructions } from '../../applications/fleet/sections/agents/agent_requirements_page/components'; @@ -92,9 +97,13 @@ describe('', () => { setPlatform: jest.fn(), }); + (useGetAgents as jest.Mock).mockReturnValue({ + data: { items: [{ policy_id: 'fleet-server-policy' }] }, + }); + await act(async () => { testBed = await setup({ - agentPolicies: [], + agentPolicies: [{ id: 'fleet-server-policy' } as AgentPolicy], onClose: jest.fn(), }); testBed.component.update(); @@ -118,6 +127,7 @@ describe('', () => { jest.clearAllMocks(); await act(async () => { testBed = await setup({ + agentPolicies: [{ id: 'fleet-server-policy' } as AgentPolicy], agentPolicy: testAgentPolicy, onClose: jest.fn(), }); @@ -139,7 +149,7 @@ describe('', () => { jest.clearAllMocks(); await act(async () => { testBed = await setup({ - agentPolicies: [], + agentPolicies: [{ id: 'fleet-server-policy' } as AgentPolicy], onClose: jest.fn(), viewDataStep: { title: 'View Data', children:
}, }); @@ -159,7 +169,7 @@ describe('', () => { jest.clearAllMocks(); await act(async () => { testBed = await setup({ - agentPolicies: [], + agentPolicies: [{ id: 'fleet-server-policy' } as AgentPolicy], onClose: jest.fn(), viewDataStep: undefined, }); @@ -190,6 +200,7 @@ describe('', () => { jest.clearAllMocks(); await act(async () => { testBed = await setup({ + agentPolicies: [{ id: 'fleet-server-policy' } as AgentPolicy], agentPolicy: testAgentPolicy, onClose: jest.fn(), }); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx new file mode 100644 index 0000000000000..80ab845aaa49c --- /dev/null +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx @@ -0,0 +1,94 @@ +/* + * 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, { useState, useCallback, useEffect } from 'react'; + +import { + AgentPolicyCreatedCallOut, + CREATE_STATUS, +} from '../../applications/fleet/sections/agents/components'; +import { AgentPolicyCreateInlineForm } from '../../applications/fleet/sections/agent_policy/components'; +import type { AgentPolicy } from '../../types'; +import { incrementPolicyName } from '../../services'; + +import { EnrollmentStepAgentPolicy } from '.'; + +interface Props { + agentPolicies: AgentPolicy[]; + excludeFleetServer?: boolean; + onAgentPolicyChange: (key?: string, policy?: AgentPolicy) => void; + withKeySelection: boolean; + selectedApiKeyId?: string; + onKeyChange?: (key?: string) => void; + isFleetServerPolicy?: boolean; +} + +export const SelectCreateAgentPolicy: React.FC = ({ + agentPolicies, + excludeFleetServer, + onAgentPolicyChange, + withKeySelection, + selectedApiKeyId, + onKeyChange, + isFleetServerPolicy, +}) => { + const [showCreatePolicy, setShowCreatePolicy] = useState(agentPolicies.length === 0); + + const [createStatus, setCreateStatus] = useState(CREATE_STATUS.INITIAL); + + const [newName, setNewName] = useState(incrementPolicyName(agentPolicies, isFleetServerPolicy)); + + const [selectedAgentPolicy, setSelectedAgentPolicy] = useState(undefined); + + useEffect(() => { + setShowCreatePolicy(agentPolicies.length === 0); + setNewName(incrementPolicyName(agentPolicies, isFleetServerPolicy)); + }, [agentPolicies, isFleetServerPolicy]); + + const onAgentPolicyCreated = useCallback( + async (policy: AgentPolicy | null) => { + if (!policy) { + setCreateStatus(CREATE_STATUS.FAILED); + return; + } + setShowCreatePolicy(false); + setCreateStatus(CREATE_STATUS.CREATED); + if (onAgentPolicyChange) { + onAgentPolicyChange(policy.id, policy!); + } + setSelectedAgentPolicy(policy.id); + }, + [onAgentPolicyChange] + ); + + return ( + <> + {showCreatePolicy ? ( + + ) : ( + setShowCreatePolicy(true)} + selectedAgentPolicy={selectedAgentPolicy} + isFleetServerPolicy={isFleetServerPolicy} + /> + )} + {createStatus !== CREATE_STATUS.INITIAL && ( + + )} + + ); +}; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.test.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.test.tsx new file mode 100644 index 0000000000000..b84d101fdef5f --- /dev/null +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.test.tsx @@ -0,0 +1,64 @@ +/* + * 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 { act } from '@testing-library/react'; + +import type { TestRenderer } from '../../mock'; +import { createFleetTestRendererMock } from '../../mock'; + +import type { AgentPolicy } from '../../types'; + +import { EnrollmentStepAgentPolicy } from '.'; + +describe('step select agent policy', () => { + let testRenderer: TestRenderer; + let renderResult: ReturnType; + let agentPolicies: AgentPolicy[] = []; + const render = () => + (renderResult = testRenderer.render( + + )); + + beforeEach(() => { + testRenderer = createFleetTestRendererMock(); + }); + + test('should not select agent policy by default if multiple exists', async () => { + agentPolicies = [ + { id: 'policy-1', name: 'Policy 1' } as AgentPolicy, + { id: 'policy-2', name: 'Policy 2' } as AgentPolicy, + ]; + + render(); + + await act(async () => { + const select = renderResult.container.querySelector('[data-test-subj="agentPolicyDropdown"]'); + expect((select as any)?.value).toEqual(''); + + expect(renderResult.getAllByRole('option').length).toBe(2); + }); + }); + + test('should select agent policy by default if one exists', async () => { + agentPolicies = [{ id: 'policy-1', name: 'Policy 1' } as AgentPolicy]; + + render(); + + await act(async () => { + const select = renderResult.container.querySelector('[data-test-subj="agentPolicyDropdown"]'); + expect((select as any)?.value).toEqual('policy-1'); + }); + }); +}); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.tsx index bc17f8eb3fbcf..539f9f990262c 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.tsx @@ -6,19 +6,39 @@ */ import React, { useState, useEffect } from 'react'; +import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiSelect, EuiSpacer, EuiText } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiLink, + EuiSelect, + EuiSpacer, + EuiText, +} from '@elastic/eui'; import type { AgentPolicy } from '../../types'; import { AgentPolicyPackageBadges } from '../agent_policy_package_badges'; +import { useAuthz } from '../../hooks'; + import { AdvancedAgentAuthenticationSettings } from './advanced_agent_authentication_settings'; +const AgentPolicyFormRow = styled(EuiFormRow)` + .euiFormRow__label { + width: 100%; + } +`; + type Props = { - agentPolicies?: AgentPolicy[]; - onAgentPolicyChange?: (key?: string) => void; + agentPolicies: AgentPolicy[]; + onAgentPolicyChange: (key?: string, policy?: AgentPolicy) => void; excludeFleetServer?: boolean; + onClickCreatePolicy: () => void; + selectedAgentPolicy?: string; + isFleetServerPolicy?: boolean; } & ( | { withKeySelection: true; @@ -38,23 +58,27 @@ const resolveAgentId = ( if (agentPolicies.length === 1) { return agentPolicies[0].id; } - - const defaultAgentPolicy = agentPolicies.find((agentPolicy) => agentPolicy.is_default); - if (defaultAgentPolicy) { - return defaultAgentPolicy.id; - } } return selectedAgentPolicyId; }; export const EnrollmentStepAgentPolicy: React.FC = (props) => { - const { agentPolicies, onAgentPolicyChange, excludeFleetServer } = props; + const { + agentPolicies, + onAgentPolicyChange, + excludeFleetServer, + onClickCreatePolicy, + selectedAgentPolicy, + isFleetServerPolicy, + } = props; const [selectedAgentPolicyId, setSelectedAgentPolicyId] = useState( () => resolveAgentId(agentPolicies, undefined) // no agent id selected yet ); + const hasFleetAllPrivileges = useAuthz().fleet.all; + useEffect( function triggerOnAgentPolicyChangeEffect() { if (onAgentPolicyChange) { @@ -74,35 +98,83 @@ export const EnrollmentStepAgentPolicy: React.FC = (props) => { [agentPolicies, selectedAgentPolicyId] ); + useEffect(() => { + if (selectedAgentPolicy) setSelectedAgentPolicyId(selectedAgentPolicy); + }, [selectedAgentPolicy]); + return ( <> - - - + + {isFleetServerPolicy ? ( + + ) : ( + + + + ), + }} + /> + )} + + + +
+ + + +
+
+ } - isLoading={!agentPolicies} - options={(agentPolicies || []).map((agentPolicy) => ({ - value: agentPolicy.id, - text: agentPolicy.name, - }))} - value={selectedAgentPolicyId || undefined} - onChange={(e) => setSelectedAgentPolicyId(e.target.value)} - aria-label={i18n.translate('xpack.fleet.enrollmentStepAgentPolicy.policySelectAriaLabel', { - defaultMessage: 'Agent policy', - })} - /> - - {selectedAgentPolicyId && ( - + ({ + value: agentPolicy.id, + text: agentPolicy.name, + }))} + value={selectedAgentPolicyId || undefined} + onChange={(e) => setSelectedAgentPolicyId(e.target.value)} + aria-label={i18n.translate( + 'xpack.fleet.enrollmentStepAgentPolicy.policySelectAriaLabel', + { + defaultMessage: 'Agent policy', + } + )} + hasNoInitialSelection={agentPolicies.length > 1} + data-test-subj="agentPolicyDropdown" + isInvalid={!selectedAgentPolicyId} /> +
+ {selectedAgentPolicyId && !isFleetServerPolicy && ( + <> + + + )} {props.withKeySelection && props.onKeyChange && ( <> diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx index 5c9c20329699b..b740d0ea62f0a 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx @@ -39,6 +39,7 @@ export interface Props extends BaseProps { } export * from './agent_policy_selection'; +export * from './agent_policy_select_create'; export * from './managed_instructions'; export * from './standalone_instructions'; export * from './steps'; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx index a78d2df06b486..6d65476e3641f 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx @@ -11,7 +11,7 @@ import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/st import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { useGetOneEnrollmentAPIKey, useLink, useFleetStatus } from '../../hooks'; +import { useGetOneEnrollmentAPIKey, useLink, useFleetStatus, useGetAgents } from '../../hooks'; import { ManualInstructions } from '../../components/enrollment_instructions'; import { @@ -23,6 +23,10 @@ import { } from '../../applications/fleet/sections/agents/agent_requirements_page/components'; import { FleetServerRequirementPage } from '../../applications/fleet/sections/agents/agent_requirements_page'; +import { policyHasFleetServer } from '../../applications/fleet/sections/agents/services/has_fleet_server'; + +import { FLEET_SERVER_PACKAGE } from '../../constants'; + import { DownloadStep, AgentPolicySelectionStep, AgentEnrollmentKeySelectionStep } from './steps'; import type { BaseProps } from './types'; @@ -71,6 +75,21 @@ export const ManagedInstructions = React.memo( const apiKey = useGetOneEnrollmentAPIKey(selectedApiKeyId); const fleetServerInstructions = useFleetServerInstructions(apiKey?.data?.item?.policy_id); + const { data: agents, isLoading: isLoadingAgents } = useGetAgents({ + page: 1, + perPage: 1000, + showInactive: false, + }); + + const fleetServers = useMemo(() => { + const fleetServerAgentPolicies: string[] = (agentPolicies ?? []) + .filter((pol) => policyHasFleetServer(pol)) + .map((pol) => pol.id); + return (agents?.items ?? []).filter((agent) => + fleetServerAgentPolicies.includes(agent.policy_id ?? '') + ); + }, [agents, agentPolicies]); + const fleetServerSteps = useMemo(() => { const { serviceToken, @@ -101,6 +120,7 @@ export const ManagedInstructions = React.memo( selectedApiKeyId, setSelectedAPIKeyId, setSelectedPolicyId, + excludeFleetServer: true, }) : AgentEnrollmentKeySelectionStep({ agentPolicy, selectedApiKeyId, setSelectedAPIKeyId }), DownloadStep(isFleetServerPolicySelected || false), @@ -140,7 +160,7 @@ export const ManagedInstructions = React.memo( return null; } - if (fleetStatus.isReady) { + if (fleetStatus.isReady && (isLoadingAgents || fleetServers.length > 0)) { return ( <> @@ -155,10 +175,13 @@ export const ManagedInstructions = React.memo( ); } + const showFleetMissingRequirements = + fleetServers.length === 0 || + (fleetStatus.missingRequirements ?? []).some((r) => r === FLEET_SERVER_PACKAGE); + return ( <> - {fleetStatus.missingRequirements?.length === 1 && - fleetStatus.missingRequirements[0] === 'fleet_server' ? ( + {showFleetMissingRequirements ? ( ) : ( diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx index 8cdae7eaf90be..953918a10f157 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { EuiText, EuiButton, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; @@ -16,8 +16,10 @@ import semverPatch from 'semver/functions/patch'; import type { AgentPolicy } from '../../types'; import { useKibanaVersion } from '../../hooks'; -import { EnrollmentStepAgentPolicy } from './agent_policy_selection'; +import { policyHasFleetServer } from '../../applications/fleet/sections/agents/services/has_fleet_server'; + import { AdvancedAgentAuthenticationSettings } from './advanced_agent_authentication_settings'; +import { SelectCreateAgentPolicy } from './agent_policy_select_create'; export const DownloadStep = (hasFleetServer: boolean) => { const kibanaVersion = useKibanaVersion(); @@ -86,37 +88,42 @@ export const AgentPolicySelectionStep = ({ setSelectedAPIKeyId?: (key?: string) => void; excludeFleetServer?: boolean; }) => { + const [agentPolicyList, setAgentPolicyList] = useState(agentPolicies || []); + const regularAgentPolicies = useMemo(() => { - return Array.isArray(agentPolicies) - ? agentPolicies.filter( - (policy) => - policy && !policy.is_managed && (!excludeFleetServer || !policy.is_default_fleet_server) - ) - : []; - }, [agentPolicies, excludeFleetServer]); + return agentPolicyList.filter( + (policy) => + policy && !policy.is_managed && (!excludeFleetServer || !policyHasFleetServer(policy)) + ); + }, [agentPolicyList, excludeFleetServer]); const onAgentPolicyChange = useCallback( - async (policyId?: string) => { + async (key?: string, policy?: AgentPolicy) => { + if (policy) { + setAgentPolicyList([...agentPolicyList, policy]); + } if (setSelectedPolicyId) { - setSelectedPolicyId(policyId); + setSelectedPolicyId(key); } }, - [setSelectedPolicyId] + [setSelectedPolicyId, setAgentPolicyList, agentPolicyList] ); return { title: i18n.translate('xpack.fleet.agentEnrollment.stepChooseAgentPolicyTitle', { - defaultMessage: 'Choose an agent policy', + defaultMessage: 'What type of host are you adding?', }), children: ( - + <> + + ), }; }; diff --git a/x-pack/plugins/fleet/public/components/agent_policy_package_badge.tsx b/x-pack/plugins/fleet/public/components/agent_policy_package_badge.tsx new file mode 100644 index 0000000000000..01cd86064e6e6 --- /dev/null +++ b/x-pack/plugins/fleet/public/components/agent_policy_package_badge.tsx @@ -0,0 +1,48 @@ +/* + * 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 { EuiFlexGroup, EuiFlexItem, EuiBadge } from '@elastic/eui'; + +import { PackageIcon } from '../components'; +import { FLEET_SERVER_PACKAGE } from '../../common/constants'; + +interface Props { + excludeFleetServer?: boolean; + pkgName: string; + pkgVersion?: string; + pkgTitle: string; +} + +export const AgentPolicyPackageBadge: React.FunctionComponent = ({ + excludeFleetServer, + pkgName, + pkgVersion, + pkgTitle, +}) => { + return ( + + + + + // this collides with some EuiText (+img) CSS from the EuiIcon component + // which makes the button large, wide, and poorly layed out + // override those styles until the bug is fixed or we find a better approach + { margin: 'unset', width: '16px' } + } + /> + + {pkgTitle} + + + ); +}; diff --git a/x-pack/plugins/fleet/public/components/agent_policy_package_badges.tsx b/x-pack/plugins/fleet/public/components/agent_policy_package_badges.tsx index b8dbe74a9f242..a6434b0c08b50 100644 --- a/x-pack/plugins/fleet/public/components/agent_policy_package_badges.tsx +++ b/x-pack/plugins/fleet/public/components/agent_policy_package_badges.tsx @@ -8,13 +8,14 @@ import React, { useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiSpacer, EuiText, EuiFlexGroup, EuiFlexItem, EuiBadge, EuiCallOut } from '@elastic/eui'; +import { EuiSpacer, EuiText, EuiFlexGroup, EuiFlexItem, EuiCallOut } from '@elastic/eui'; import { FLEET_SERVER_PACKAGE } from '../../common/constants'; import type { PackagePolicy, PackagePolicyPackage } from '../types'; import { useGetOneAgentPolicy } from '../hooks'; -import { PackageIcon } from '../components'; + +import { AgentPolicyPackageBadge } from './agent_policy_package_badge'; interface Props { agentPolicyId: string; @@ -88,34 +89,13 @@ export const AgentPolicyPackageBadges: React.FunctionComponent = ({ {packages.map((pkg, idx) => { return ( - - - - - // this collides with some EuiText (+img) CSS from the EuiIcon component - // which makes the button large, wide, and poorly layed out - // override those styles until the bug is fixed or we find a better approach - { margin: 'unset', width: '16px' } - } - /> - - {pkg.title} - - + excludeFleetServer={excludeFleetServer} + pkgName={pkg.name} + pkgVersion={pkg.version} + pkgTitle={pkg.title} + /> ); })} diff --git a/x-pack/plugins/fleet/public/components/context_menu_actions.tsx b/x-pack/plugins/fleet/public/components/context_menu_actions.tsx index f25415101b9ac..5bfb12f865388 100644 --- a/x-pack/plugins/fleet/public/components/context_menu_actions.tsx +++ b/x-pack/plugins/fleet/public/components/context_menu_actions.tsx @@ -67,6 +67,7 @@ export const ContextMenuActions = React.memo(({ button, onChange, isOpen, aria-label={i18n.translate('xpack.fleet.genericActionsMenuText', { defaultMessage: 'Open', })} + data-test-subj="agentActionsBtn" /> ) } diff --git a/x-pack/plugins/fleet/public/components/index.ts b/x-pack/plugins/fleet/public/components/index.ts index 757625d7244a3..ce6e09f33eb62 100644 --- a/x-pack/plugins/fleet/public/components/index.ts +++ b/x-pack/plugins/fleet/public/components/index.ts @@ -16,6 +16,7 @@ export { AlphaFlyout } from './alpha_flyout'; export type { HeaderProps } from './header'; export { Header } from './header'; export { NewEnrollmentTokenModal } from './new_enrollment_key_modal'; +export { AgentPolicyPackageBadge } from './agent_policy_package_badge'; export { AgentPolicyPackageBadges } from './agent_policy_package_badges'; export { DangerEuiContextMenuItem } from './danger_eui_context_menu_item'; export { PackagePolicyDeleteProvider } from './package_policy_delete_provider'; diff --git a/x-pack/plugins/fleet/public/components/link_and_revision.tsx b/x-pack/plugins/fleet/public/components/link_and_revision.tsx index 99f85a09356c8..b56787fbb399c 100644 --- a/x-pack/plugins/fleet/public/components/link_and_revision.tsx +++ b/x-pack/plugins/fleet/public/components/link_and_revision.tsx @@ -26,6 +26,7 @@ export const AgentPolicySummaryLine = memo<{ policy: AgentPolicy }>(({ policy }) className={`eui-textTruncate`} href={getHref('policy_details', { policyId: id })} title={name || id} + data-test-subj="agentPolicyNameLink" > {name || id} diff --git a/x-pack/plugins/fleet/public/components/new_enrollment_key_modal.tsx b/x-pack/plugins/fleet/public/components/new_enrollment_key_modal.tsx index 82332784839a9..9d71a50ce026c 100644 --- a/x-pack/plugins/fleet/public/components/new_enrollment_key_modal.tsx +++ b/x-pack/plugins/fleet/public/components/new_enrollment_key_modal.tsx @@ -76,13 +76,8 @@ export const NewEnrollmentTokenModal: React.FunctionComponent = ({ agentPolicies = [], }) => { const { notifications } = useStartServices(); - const policyIdDefaultValue = agentPolicies.find((agentPolicy) => agentPolicy.is_default)?.id; const form = useCreateApiKeyForm( - policyIdDefaultValue - ? policyIdDefaultValue - : agentPolicies.length > 0 - ? agentPolicies[0].id - : undefined, + agentPolicies.length > 0 ? agentPolicies[0].id : undefined, (key: EnrollmentAPIKey) => { onClose(key); notifications.toasts.addSuccess( diff --git a/x-pack/plugins/fleet/public/components/package_policy_actions_menu.test.tsx b/x-pack/plugins/fleet/public/components/package_policy_actions_menu.test.tsx index 220e5ddb8f547..7b260f1e27059 100644 --- a/x-pack/plugins/fleet/public/components/package_policy_actions_menu.test.tsx +++ b/x-pack/plugins/fleet/public/components/package_policy_actions_menu.test.tsx @@ -48,7 +48,6 @@ function createMockAgentPolicy(props: Partial = {}): AgentPolicy { monitoring_enabled: [], name: 'Test Policy', description: '', - is_default: false, is_preconfigured: false, status: 'active', is_managed: false, diff --git a/x-pack/plugins/fleet/public/constants/index.ts b/x-pack/plugins/fleet/public/constants/index.ts index 139f9d3d1f1c4..dddd7552f0151 100644 --- a/x-pack/plugins/fleet/public/constants/index.ts +++ b/x-pack/plugins/fleet/public/constants/index.ts @@ -20,7 +20,6 @@ export { ENROLLMENT_API_KEYS_INDEX, // Preconfiguration AUTO_UPDATE_PACKAGES, - DEFAULT_PACKAGES, KEEP_POLICIES_UP_TO_DATE_PACKAGES, AUTO_UPGRADE_POLICIES_PACKAGES, } from '../../common/constants'; diff --git a/x-pack/plugins/fleet/public/services/increment_policy_name.test.ts b/x-pack/plugins/fleet/public/services/increment_policy_name.test.ts new file mode 100644 index 0000000000000..363ff26a6fe51 --- /dev/null +++ b/x-pack/plugins/fleet/public/services/increment_policy_name.test.ts @@ -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 { incrementPolicyName } from './increment_policy_name'; +describe('increment policy name', () => { + it('should return index 1 when no policies', () => { + const name = incrementPolicyName([]); + expect(name).toEqual('Agent policy 1'); + }); + + it('should return index 1 when policies with other name', () => { + const name = incrementPolicyName([{ name: 'policy' } as any]); + expect(name).toEqual('Agent policy 1'); + }); + + it('should return index 2 when policy 1 exists', () => { + const name = incrementPolicyName([{ name: 'Agent policy 1' } as any]); + expect(name).toEqual('Agent policy 2'); + }); + + it('should return index 11 when policy 10 is max', () => { + const name = incrementPolicyName([ + { name: 'Agent policy 10' } as any, + { name: 'Agent policy 9' } as any, + { name: 'policy' } as any, + ]); + expect(name).toEqual('Agent policy 11'); + }); + + it('should return index 2 when policy 1 exists - fleet server', () => { + const name = incrementPolicyName([{ name: 'Fleet Server policy 1' } as any], true); + expect(name).toEqual('Fleet Server policy 2'); + }); +}); diff --git a/x-pack/plugins/fleet/public/services/increment_policy_name.ts b/x-pack/plugins/fleet/public/services/increment_policy_name.ts new file mode 100644 index 0000000000000..77a9baff1805d --- /dev/null +++ b/x-pack/plugins/fleet/public/services/increment_policy_name.ts @@ -0,0 +1,21 @@ +/* + * 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. + */ + +export function incrementPolicyName( + policies: Array<{ name: string }>, + isFleetServerPolicy?: boolean +): string { + const indices = policies + .map((pol: { name: string }) => pol.name) + .map((name) => { + const match = name.match( + isFleetServerPolicy ? /Fleet Server policy (\d+)/ : /Agent policy (\d+)/ + ); + return match ? parseInt(match[1], 10) : 0; + }); + return `${isFleetServerPolicy ? 'Fleet Server' : 'Agent'} policy ${Math.max(...indices, 0) + 1}`; +} diff --git a/x-pack/plugins/fleet/public/services/index.ts b/x-pack/plugins/fleet/public/services/index.ts index 10dfe6b59d6ba..ad108e3f97ca9 100644 --- a/x-pack/plugins/fleet/public/services/index.ts +++ b/x-pack/plugins/fleet/public/services/index.ts @@ -44,3 +44,4 @@ export { export * from './pkg_key_from_package_info'; export * from './ui_extensions'; +export * from './increment_policy_name'; diff --git a/x-pack/plugins/fleet/server/constants/index.ts b/x-pack/plugins/fleet/server/constants/index.ts index 0769f02fc0272..0ccbeb9f025e4 100644 --- a/x-pack/plugins/fleet/server/constants/index.ts +++ b/x-pack/plugins/fleet/server/constants/index.ts @@ -43,11 +43,8 @@ export { ASSETS_SAVED_OBJECT_TYPE, GLOBAL_SETTINGS_SAVED_OBJECT_TYPE, // Defaults - DEFAULT_AGENT_POLICY, - DEFAULT_FLEET_SERVER_AGENT_POLICY, DEFAULT_OUTPUT, DEFAULT_OUTPUT_ID, - DEFAULT_PACKAGES, PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES, AGENT_POLICY_DEFAULT_MONITORING_DATASETS, // Fleet Server index diff --git a/x-pack/plugins/fleet/server/index.ts b/x-pack/plugins/fleet/server/index.ts index 7bf3ca161f4ee..550d6acaffc7e 100644 --- a/x-pack/plugins/fleet/server/index.ts +++ b/x-pack/plugins/fleet/server/index.ts @@ -54,6 +54,39 @@ export const config: PluginConfigDescriptor = { unused('agents.pollingRequestTimeout', { level: 'critical' }), unused('agents.tlsCheckDisabled', { level: 'critical' }), unused('agents.fleetServerEnabled', { level: 'critical' }), + // Deprecate default policy flags + (fullConfig, fromPath, addDeprecation) => { + if ( + (fullConfig?.xpack?.fleet?.agentPolicies || []).find((policy: any) => policy.is_default) + ) { + addDeprecation({ + configPath: 'xpack.fleet.agentPolicies.is_default', + message: `Config key [xpack.fleet.agentPolicies.is_default] is deprecated.`, + correctiveActions: { + manualSteps: [`Create a dedicated policy instead through the UI or API.`], + }, + level: 'warning', + }); + } + return fullConfig; + }, + (fullConfig, fromPath, addDeprecation) => { + if ( + (fullConfig?.xpack?.fleet?.agentPolicies || []).find( + (policy: any) => policy.is_default_fleet_server + ) + ) { + addDeprecation({ + configPath: 'xpack.fleet.agentPolicies.is_default_fleet_server', + message: `Config key [xpack.fleet.agentPolicies.is_default_fleet_server] is deprecated.`, + correctiveActions: { + manualSteps: [`Create a dedicated fleet server policy instead through the UI or API.`], + }, + level: 'warning', + }); + } + return fullConfig; + }, // Renaming elasticsearch.host => elasticsearch.hosts (fullConfig, fromPath, addDeprecation) => { const oldValue = fullConfig?.xpack?.fleet?.agents?.elasticsearch?.host; diff --git a/x-pack/plugins/fleet/server/integration_tests/reset_preconfiguration.test.ts b/x-pack/plugins/fleet/server/integration_tests/reset_preconfiguration.test.ts index 2ef6d8095b9f3..9efbacfae17bf 100644 --- a/x-pack/plugins/fleet/server/integration_tests/reset_preconfiguration.test.ts +++ b/x-pack/plugins/fleet/server/integration_tests/reset_preconfiguration.test.ts @@ -42,7 +42,7 @@ const waitForFleetSetup = async (root: Root) => { } }; -describe('Fleet preconfiguration rest', () => { +describe('Fleet preconfiguration reset', () => { let esServer: kbnTestServer.TestElasticsearchUtils; let kbnServer: kbnTestServer.TestKibanaUtils; @@ -63,6 +63,12 @@ describe('Fleet preconfiguration rest', () => { { xpack: { fleet: { + packages: [ + { + name: 'fleet_server', + version: 'latest', + }, + ], // Preconfigure two policies test-12345 and test-456789 agentPolicies: [ { diff --git a/x-pack/plugins/fleet/server/mocks/index.ts b/x-pack/plugins/fleet/server/mocks/index.ts index 684f84a9b48a5..7c4c822bd550a 100644 --- a/x-pack/plugins/fleet/server/mocks/index.ts +++ b/x-pack/plugins/fleet/server/mocks/index.ts @@ -127,7 +127,6 @@ export const createMockAgentPolicyService = (): jest.Mocked undefined) - : undefined, - ]); - - // Create the system monitoring package policy and add it to agent policy. - if (withSysMonitoring && newSysPackagePolicy !== undefined && agentPolicy !== undefined) { - newSysPackagePolicy.policy_id = agentPolicy.id; - newSysPackagePolicy.namespace = agentPolicy.namespace; - newSysPackagePolicy.name = await incrementPackageName(soClient, FLEET_SYSTEM_PACKAGE); - - await packagePolicyService.create(soClient, esClient, newSysPackagePolicy, { + const body: CreateAgentPolicyResponse = { + item: await createAgentPolicyWithPackages({ + soClient, + esClient, + newPolicy, + hasFleetServer, + withSysMonitoring, + monitoringEnabled, spaceId, user, - bumpRevision: false, - }); - } - - await agentPolicyService.createFleetServerPolicy(soClient, agentPolicy.id); - - const body: CreateAgentPolicyResponse = { - item: agentPolicy, + }), }; return response.ok({ diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 96cb51af53b45..e91eaf0bb0569 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -77,9 +77,9 @@ const getSavedObjectTypes = ( name: { type: 'keyword' }, description: { type: 'text' }, namespace: { type: 'keyword' }, + is_managed: { type: 'boolean' }, is_default: { type: 'boolean' }, is_default_fleet_server: { type: 'boolean' }, - is_managed: { type: 'boolean' }, status: { type: 'keyword' }, package_policies: { type: 'keyword' }, unenroll_timeout: { type: 'integer' }, diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/to_v7_16_0.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v7_16_0.ts index c435e504a2bfe..66331dab47cc8 100644 --- a/x-pack/plugins/fleet/server/saved_objects/migrations/to_v7_16_0.ts +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v7_16_0.ts @@ -8,7 +8,12 @@ import type { SavedObjectMigrationFn } from 'kibana/server'; import type { Installation, PackagePolicy } from '../../../common'; -import { DEFAULT_PACKAGES } from '../../../common'; +import { + FLEET_ELASTIC_AGENT_PACKAGE, + FLEET_SERVER_PACKAGE, + FLEET_SYSTEM_PACKAGE, +} from '../../../common'; +import { PRECONFIGURATION_LATEST_KEYWORD } from '../../constants'; import { migratePackagePolicyToV7160 as SecSolMigratePackagePolicyToV7160 } from './security_solution'; @@ -18,6 +23,15 @@ export const migrateInstallationToV7160: SavedObjectMigrationFn { const updatedInstallationDoc = installationDoc; + const DEFAULT_PACKAGES = [ + FLEET_SYSTEM_PACKAGE, + FLEET_ELASTIC_AGENT_PACKAGE, + FLEET_SERVER_PACKAGE, + ].map((name) => ({ + name, + version: PRECONFIGURATION_LATEST_KEYWORD, + })); + if (DEFAULT_PACKAGES.some((pkg) => pkg.name === updatedInstallationDoc.attributes.name)) { updatedInstallationDoc.attributes.keep_policies_up_to_date = true; } diff --git a/x-pack/plugins/fleet/server/services/agent_policy.test.ts b/x-pack/plugins/fleet/server/services/agent_policy.test.ts index a8c91f31f72ad..11d710e9e2b3d 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.test.ts @@ -5,10 +5,10 @@ * 2.0. */ -import uuidv5 from 'uuid/v5'; - import { elasticsearchServiceMock, savedObjectsClientMock } from 'src/core/server/mocks'; +import { SavedObjectsErrorHelpers } from 'src/core/server'; + import type { AgentPolicy, FullAgentPolicy, @@ -237,7 +237,7 @@ describe('agent policy', () => { }); }); - describe('createFleetServerPolicy', () => { + describe('deployPolicy', () => { beforeEach(() => { mockedGetFullAgentPolicy.mockReset(); }); @@ -254,7 +254,7 @@ describe('agent policy', () => { type: 'mocked', references: [], }); - await agentPolicyService.createFleetServerPolicy(soClient, 'policy123'); + await agentPolicyService.deployPolicy(soClient, 'policy123'); expect(esClient.create).not.toBeCalled(); }); @@ -280,7 +280,7 @@ describe('agent policy', () => { type: 'mocked', references: [], }); - await agentPolicyService.createFleetServerPolicy(soClient, 'policy123'); + await agentPolicyService.deployPolicy(soClient, 'policy123'); expect(esClient.create).toBeCalledWith( expect.objectContaining({ @@ -297,14 +297,13 @@ describe('agent policy', () => { }); describe('ensurePreconfiguredAgentPolicy', () => { - it('should use preconfigured id if provided for default policy', async () => { + it('should use preconfigured id if provided for policy', async () => { const soClient = savedObjectsClientMock.create(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; const preconfiguredAgentPolicy: PreconfiguredAgentPolicy = { id: 'my-unique-id', name: 'My Preconfigured Policy', - is_default: true, package_policies: [ { name: 'my-package-policy', @@ -317,6 +316,7 @@ describe('agent policy', () => { }; soClient.find.mockResolvedValueOnce({ total: 0, saved_objects: [], page: 1, per_page: 10 }); + soClient.get.mockRejectedValueOnce(SavedObjectsErrorHelpers.createGenericNotFoundError()); soClient.create.mockResolvedValueOnce({ id: 'my-unique-id', @@ -338,47 +338,5 @@ describe('agent policy', () => { ); }); }); - - it('should generate uuid if no id is provided for default policy', async () => { - const soClient = savedObjectsClientMock.create(); - const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; - - const preconfiguredAgentPolicy = { - name: 'My Preconfigured Policy', - is_default: true, - package_policies: [ - { - name: 'my-package-policy', - id: 'my-package-policy-id', - package: { - name: 'test-package', - }, - }, - ], - }; - - (uuidv5 as unknown as jest.Mock).mockReturnValueOnce('fake-uuid'); - - soClient.find.mockResolvedValueOnce({ total: 0, saved_objects: [], page: 1, per_page: 10 }); - - soClient.create.mockResolvedValueOnce({ - id: 'my-unique-id', - type: AGENT_POLICY_SAVED_OBJECT_TYPE, - attributes: {}, - references: [], - }); - - await agentPolicyService.ensurePreconfiguredAgentPolicy( - soClient, - esClient, - preconfiguredAgentPolicy as any - ); - - expect(soClient.create).toHaveBeenCalledWith( - AGENT_POLICY_SAVED_OBJECT_TYPE, - expect.anything(), - expect.objectContaining({ id: 'fake-uuid' }) - ); - }); }); }); diff --git a/x-pack/plugins/fleet/server/services/agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policy.ts index 4c8ef5c3a1b87..d473504622bad 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.ts @@ -16,8 +16,6 @@ import type { SavedObjectsBulkUpdateResponse, } from 'src/core/server'; -import { SavedObjectsErrorHelpers } from '../../../../../src/core/server'; - import type { AuthenticatedUser } from '../../../security/server'; import { AGENT_POLICY_SAVED_OBJECT_TYPE, @@ -134,91 +132,31 @@ class AgentPolicyService { is_preconfigured: true, }; - let searchParams; - - const isDefaultPolicy = - preconfiguredAgentPolicy.is_default || preconfiguredAgentPolicy.is_default_fleet_server; + if (!id) throw new Error('Missing ID'); - if (isDefaultPolicy) { - searchParams = { - searchFields: [ - preconfiguredAgentPolicy.is_default_fleet_server - ? 'is_default_fleet_server' - : 'is_default', - ], - search: 'true', - }; - } else if (id) { - searchParams = { - id: String(id), - }; - } - - if (!searchParams) throw new Error('Missing ID'); - - return await this.ensureAgentPolicy(soClient, esClient, newAgentPolicy, searchParams, id); + return await this.ensureAgentPolicy(soClient, esClient, newAgentPolicy, id as string); } private async ensureAgentPolicy( soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, newAgentPolicy: NewAgentPolicy, - searchParams: - | { id: string } - | { - searchFields: string[]; - search: string; - }, - id?: string | number + id: string ): Promise<{ created: boolean; policy: AgentPolicy; }> { // For preconfigured policies with a specified ID - if ('id' in searchParams) { - try { - const agentPolicy = await soClient.get( - AGENT_POLICY_SAVED_OBJECT_TYPE, - searchParams.id - ); - return { - created: false, - policy: { - id: agentPolicy.id, - ...agentPolicy.attributes, - }, - }; - } catch (e) { - if (SavedObjectsErrorHelpers.isNotFoundError(e)) { - return { - created: true, - policy: await this.create(soClient, esClient, newAgentPolicy, { id: searchParams.id }), - }; - } else throw e; - } - } - - // For default policies without a specified ID - const agentPolicies = await soClient.find({ - type: AGENT_POLICY_SAVED_OBJECT_TYPE, - ...searchParams, - }); - - if (agentPolicies.total === 0) { + const agentPolicy = await this.get(soClient, id, false).catch(() => null); + if (!agentPolicy) { return { created: true, - policy: await this.create(soClient, esClient, newAgentPolicy, { - id: id ? String(id) : uuidv5(newAgentPolicy.name, UUID_V5_NAMESPACE), - }), + policy: await this.create(soClient, esClient, newAgentPolicy, { id }), }; } - return { created: false, - policy: { - id: agentPolicies.saved_objects[0].id, - ...agentPolicies.saved_objects[0].attributes, - }, + policy: agentPolicy, }; } @@ -243,9 +181,7 @@ class AgentPolicyService { options ); - if (!agentPolicy.is_default && !agentPolicy.is_default_fleet_server) { - await this.triggerAgentPolicyUpdatedEvent(soClient, esClient, 'created', newSo.id); - } + await this.triggerAgentPolicyUpdatedEvent(soClient, esClient, 'created', newSo.id); return { id: newSo.id, ...newSo.attributes }; } @@ -454,7 +390,7 @@ class AgentPolicyService { throw new Error('Copied agent policy not found'); } - await this.createFleetServerPolicy(soClient, newAgentPolicy.id); + await this.deployPolicy(soClient, newAgentPolicy.id); return updatedAgentPolicy; } @@ -601,20 +537,6 @@ class AgentPolicyService { ); } - public async getDefaultAgentPolicyId(soClient: SavedObjectsClientContract) { - const agentPolicies = await soClient.find({ - type: AGENT_POLICY_SAVED_OBJECT_TYPE, - searchFields: ['is_default'], - search: 'true', - }); - - if (agentPolicies.saved_objects.length === 0) { - throw new Error('No default agent policy'); - } - - return agentPolicies.saved_objects[0].id; - } - public async delete( soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, @@ -630,14 +552,6 @@ class AgentPolicyService { throw new HostedAgentPolicyRestrictionRelatedError(`Cannot delete hosted agent policy ${id}`); } - if (agentPolicy.is_default && !options?.force) { - throw new Error('The default agent policy cannot be deleted'); - } - - if (agentPolicy.is_default_fleet_server && !options?.force) { - throw new Error('The default fleet server agent policy cannot be deleted'); - } - const { total } = await getAgentsByKuery(esClient, { showInactive: false, perPage: 0, @@ -688,10 +602,7 @@ class AgentPolicyService { }; } - public async createFleetServerPolicy( - soClient: SavedObjectsClientContract, - agentPolicyId: string - ) { + public async deployPolicy(soClient: SavedObjectsClientContract, agentPolicyId: string) { // Use internal ES client so we have permissions to write to .fleet* indices const esClient = appContextService.getInternalUserESClient(); const defaultOutputId = await outputService.getDefaultDataOutputId(soClient); diff --git a/x-pack/plugins/fleet/server/services/agent_policy_create.test.ts b/x-pack/plugins/fleet/server/services/agent_policy_create.test.ts new file mode 100644 index 0000000000000..57398f053b269 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/agent_policy_create.test.ts @@ -0,0 +1,211 @@ +/* + * 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 { elasticsearchServiceMock, savedObjectsClientMock } from 'src/core/server/mocks'; + +import type { AgentPolicy, PackagePolicy } from '../types'; + +import { agentPolicyService, packagePolicyService } from '.'; +import { createAgentPolicyWithPackages } from './agent_policy_create'; +import { bulkInstallPackages } from './epm/packages'; +import { incrementPackageName } from './package_policy'; + +const mockedAgentPolicyService = agentPolicyService as jest.Mocked; +const mockedPackagePolicyService = packagePolicyService as jest.Mocked; +const mockIncrementPackageName = incrementPackageName as jest.MockedFunction< + typeof incrementPackageName +>; + +jest.mock('./epm/packages', () => { + return { + bulkInstallPackages: jest.fn(), + }; +}); + +const mockedBulkInstallPackages = bulkInstallPackages as jest.Mocked; + +jest.mock('./setup', () => { + return { + ensureDefaultEnrollmentAPIKeysExists: jest.fn(), + }; +}); + +jest.mock('./agent_policy'); +jest.mock('./package_policy'); + +function getPackagePolicy(name: string, policyId = '') { + return { + name, + namespace: 'default', + enabled: true, + policy_id: policyId, + output_id: '', + inputs: [], + }; +} + +describe('createAgentPolicyWithPackages', () => { + const esClientMock = elasticsearchServiceMock.createElasticsearchClient(); + const soClientMock = savedObjectsClientMock.create(); + + beforeEach(() => { + mockedAgentPolicyService.get.mockRejectedValue({ output: { statusCode: 404 }, isBoom: true }); + mockedAgentPolicyService.create.mockImplementation((soClient, esClient, newPolicy, options) => + Promise.resolve({ + ...newPolicy, + id: options?.id || 'new_id', + } as AgentPolicy) + ); + mockedAgentPolicyService.deployPolicy.mockResolvedValue(); + + mockedPackagePolicyService.buildPackagePolicyFromPackage.mockImplementation( + (soClient, packageToInstall) => Promise.resolve(getPackagePolicy(packageToInstall)) + ); + mockIncrementPackageName.mockImplementation((soClient: any, pkg: string) => + Promise.resolve(`${pkg}-1`) + ); + mockedPackagePolicyService.create.mockImplementation((soClient, esClient, newPolicy) => + Promise.resolve({ + ...newPolicy, + } as PackagePolicy) + ); + }); + + it('should roll back agent policy if package policy creation failed', async () => { + mockedPackagePolicyService.buildPackagePolicyFromPackage.mockImplementationOnce( + (soClient, packageToInstall) => Promise.reject(new Error('error')) + ); + let error; + try { + await createAgentPolicyWithPackages({ + esClient: esClientMock, + soClient: soClientMock, + newPolicy: { name: 'Agent policy 1', namespace: 'default' }, + withSysMonitoring: true, + spaceId: 'default', + }); + } catch (err) { + error = err; + } + + expect(error.message).toEqual('error'); + + expect(mockedAgentPolicyService.delete).toHaveBeenCalledWith( + expect.anything(), + expect.anything(), + 'new_id', + expect.anything() + ); + }); + + it('should create policy with fleet_server, system and elastic_agent package - first one', async () => { + const response = await createAgentPolicyWithPackages({ + esClient: esClientMock, + soClient: soClientMock, + newPolicy: { name: 'Fleet Server policy', namespace: 'default' }, + hasFleetServer: true, + withSysMonitoring: true, + monitoringEnabled: ['logs', 'metrics'], + spaceId: 'default', + }); + + expect(response.id).toEqual('fleet-server-policy'); + expect(response.is_default_fleet_server).toBe(true); + expect(mockedBulkInstallPackages).toHaveBeenCalledWith({ + savedObjectsClient: soClientMock, + esClient: esClientMock, + packagesToInstall: ['fleet_server', 'system', 'elastic_agent'], + spaceId: 'default', + }); + expect(mockedPackagePolicyService.create).toHaveBeenCalledWith( + expect.anything(), + expect.anything(), + getPackagePolicy('system-1', 'fleet-server-policy'), + expect.anything() + ); + expect(mockedPackagePolicyService.create).toHaveBeenCalledWith( + expect.anything(), + expect.anything(), + getPackagePolicy('fleet_server-1', 'fleet-server-policy'), + expect.anything() + ); + }); + it('should create policy with fleet_server package', async () => { + mockedAgentPolicyService.get.mockResolvedValueOnce({} as AgentPolicy); + const response = await createAgentPolicyWithPackages({ + esClient: esClientMock, + soClient: soClientMock, + newPolicy: { name: 'Fleet Server policy 2', namespace: 'default' }, + hasFleetServer: true, + withSysMonitoring: false, + monitoringEnabled: [], + spaceId: 'default', + }); + + expect(response.id).toEqual('new_id'); + expect(mockedBulkInstallPackages).toHaveBeenCalledWith({ + savedObjectsClient: soClientMock, + esClient: esClientMock, + packagesToInstall: ['fleet_server'], + spaceId: 'default', + }); + expect(mockedPackagePolicyService.create).toHaveBeenCalledWith( + expect.anything(), + expect.anything(), + getPackagePolicy('fleet_server-1', 'new_id'), + expect.anything() + ); + }); + it('should create policy with system package', async () => { + const response = await createAgentPolicyWithPackages({ + esClient: esClientMock, + soClient: soClientMock, + newPolicy: { name: 'Agent policy 1', namespace: 'default' }, + withSysMonitoring: true, + spaceId: 'default', + }); + + expect(response.id).toEqual('new_id'); + expect(mockedBulkInstallPackages).toHaveBeenCalledWith({ + savedObjectsClient: soClientMock, + esClient: esClientMock, + packagesToInstall: ['system'], + spaceId: 'default', + }); + expect(mockedPackagePolicyService.create).toHaveBeenCalledWith( + expect.anything(), + expect.anything(), + getPackagePolicy('system-1', 'new_id'), + expect.anything() + ); + }); + + it('should create policy with system and elastic_agent package', async () => { + const response = await createAgentPolicyWithPackages({ + esClient: esClientMock, + soClient: soClientMock, + newPolicy: { name: 'Agent policy 1', namespace: 'default' }, + withSysMonitoring: true, + spaceId: 'default', + monitoringEnabled: ['logs'], + }); + + expect(response.id).toEqual('new_id'); + expect(mockedBulkInstallPackages).toHaveBeenCalledWith({ + savedObjectsClient: soClientMock, + esClient: esClientMock, + packagesToInstall: ['system', 'elastic_agent'], + spaceId: 'default', + }); + expect(mockedPackagePolicyService.create).toHaveBeenCalledWith( + expect.anything(), + expect.anything(), + getPackagePolicy('system-1', 'new_id'), + expect.anything() + ); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/agent_policy_create.ts b/x-pack/plugins/fleet/server/services/agent_policy_create.ts new file mode 100644 index 0000000000000..9bbf2889357a3 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/agent_policy_create.ts @@ -0,0 +1,148 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; + +import type { AuthenticatedUser } from '../../../security/common/model'; + +import { + FLEET_ELASTIC_AGENT_PACKAGE, + FLEET_SERVER_PACKAGE, + FLEET_SYSTEM_PACKAGE, +} from '../../common'; + +import type { AgentPolicy, NewAgentPolicy } from '../types'; + +import { agentPolicyService, packagePolicyService } from '.'; +import { incrementPackageName } from './package_policy'; +import { bulkInstallPackages } from './epm/packages'; +import { ensureDefaultEnrollmentAPIKeysExists } from './setup'; + +const FLEET_SERVER_POLICY_ID = 'fleet-server-policy'; + +async function getFleetServerAgentPolicyId( + soClient: SavedObjectsClientContract +): Promise { + let agentPolicyId; + // creating first fleet server policy with id 'fleet-server-policy' + let agentPolicy; + try { + agentPolicy = await agentPolicyService.get(soClient, FLEET_SERVER_POLICY_ID, false); + } catch (err) { + if (!err.isBoom || err.output.statusCode !== 404) { + throw err; + } + } + if (!agentPolicy) { + agentPolicyId = FLEET_SERVER_POLICY_ID; + } + return agentPolicyId; +} + +async function createPackagePolicy( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + agentPolicy: AgentPolicy, + packageToInstall: string, + options: { spaceId: string; user: AuthenticatedUser | undefined } +) { + const newPackagePolicy = await packagePolicyService + .buildPackagePolicyFromPackage(soClient, packageToInstall) + .catch(async (error) => { + // rollback agent policy on error + await agentPolicyService.delete(soClient, esClient, agentPolicy.id, { + force: true, + }); + throw error; + }); + + if (!newPackagePolicy) return; + + newPackagePolicy.policy_id = agentPolicy.id; + newPackagePolicy.namespace = agentPolicy.namespace; + newPackagePolicy.name = await incrementPackageName(soClient, packageToInstall); + + await packagePolicyService.create(soClient, esClient, newPackagePolicy, { + spaceId: options.spaceId, + user: options.user, + bumpRevision: false, + }); +} + +interface CreateAgentPolicyParams { + soClient: SavedObjectsClientContract; + esClient: ElasticsearchClient; + newPolicy: NewAgentPolicy; + hasFleetServer?: boolean; + withSysMonitoring: boolean; + monitoringEnabled?: string[]; + spaceId: string; + user?: AuthenticatedUser; +} + +export async function createAgentPolicyWithPackages({ + soClient, + esClient, + newPolicy, + hasFleetServer, + withSysMonitoring, + monitoringEnabled, + spaceId, + user, +}: CreateAgentPolicyParams) { + let agentPolicyId; + const packagesToInstall = []; + if (hasFleetServer) { + packagesToInstall.push(FLEET_SERVER_PACKAGE); + + agentPolicyId = await getFleetServerAgentPolicyId(soClient); + if (agentPolicyId === FLEET_SERVER_POLICY_ID) { + // setting first fleet server policy to default, so that fleet server can enroll without setting policy_id + newPolicy.is_default_fleet_server = true; + } + } + if (withSysMonitoring) { + packagesToInstall.push(FLEET_SYSTEM_PACKAGE); + } + if (monitoringEnabled?.length) { + packagesToInstall.push(FLEET_ELASTIC_AGENT_PACKAGE); + } + if (packagesToInstall.length > 0) { + await bulkInstallPackages({ + savedObjectsClient: soClient, + esClient, + packagesToInstall, + spaceId, + }); + } + + const agentPolicy = await agentPolicyService.create(soClient, esClient, newPolicy, { + user, + id: agentPolicyId, + }); + + // Create the fleet server package policy and add it to agent policy. + if (hasFleetServer) { + await createPackagePolicy(soClient, esClient, agentPolicy, FLEET_SERVER_PACKAGE, { + spaceId, + user, + }); + } + + // Create the system monitoring package policy and add it to agent policy. + if (withSysMonitoring) { + await createPackagePolicy(soClient, esClient, agentPolicy, FLEET_SYSTEM_PACKAGE, { + spaceId, + user, + }); + } + + await ensureDefaultEnrollmentAPIKeysExists(soClient, esClient); + await agentPolicyService.deployPolicy(soClient, agentPolicy.id); + + return agentPolicy; +} diff --git a/x-pack/plugins/fleet/server/services/agent_policy_update.ts b/x-pack/plugins/fleet/server/services/agent_policy_update.ts index 51bf068b8b111..30472c242ba45 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy_update.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy_update.ts @@ -42,12 +42,13 @@ export async function agentPolicyUpdateEventHandler( await generateEnrollmentAPIKey(soClient, esClient, { name: 'Default', agentPolicyId, + forceRecreate: true, }); - await agentPolicyService.createFleetServerPolicy(internalSoClient, agentPolicyId); + await agentPolicyService.deployPolicy(internalSoClient, agentPolicyId); } if (action === 'updated') { - await agentPolicyService.createFleetServerPolicy(internalSoClient, agentPolicyId); + await agentPolicyService.deployPolicy(internalSoClient, agentPolicyId); } if (action === 'deleted') { diff --git a/x-pack/plugins/fleet/server/services/agents/setup.ts b/x-pack/plugins/fleet/server/services/agents/setup.ts index 2b680dee1146e..ffbfb589b10f2 100644 --- a/x-pack/plugins/fleet/server/services/agents/setup.ts +++ b/x-pack/plugins/fleet/server/services/agents/setup.ts @@ -29,7 +29,7 @@ export async function ensureFleetServerAgentPoliciesExists( )); if (!policyChangeActionExist) { - return agentPolicyService.createFleetServerPolicy(soClient, agentPolicy.id); + return agentPolicyService.deployPolicy(soClient, agentPolicy.id); } }) ); diff --git a/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts b/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts index 76e2d02970de0..045e6495c9675 100644 --- a/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts +++ b/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts @@ -156,7 +156,7 @@ export async function generateEnrollmentAPIKey( data: { name?: string; expiration?: string; - agentPolicyId?: string; + agentPolicyId: string; forceRecreate?: boolean; } ): Promise { @@ -165,8 +165,7 @@ export async function generateEnrollmentAPIKey( if (data.agentPolicyId) { await validateAgentPolicyId(soClient, data.agentPolicyId); } - const agentPolicyId = - data.agentPolicyId ?? (await agentPolicyService.getDefaultAgentPolicyId(soClient)); + const agentPolicyId = data.agentPolicyId; if (providedKeyName && !forceRecreate) { let hasMore = true; diff --git a/x-pack/plugins/fleet/server/services/epm/packages/get.ts b/x-pack/plugins/fleet/server/services/epm/packages/get.ts index 83bb8125e077e..755fafd8fbc7a 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/get.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/get.ts @@ -28,7 +28,7 @@ import { getEsPackage } from '../archive/storage'; import { getArchivePackage } from '../archive'; import { normalizeKuery } from '../../saved_object'; -import { createInstallableFrom, isUnremovablePackage } from './index'; +import { createInstallableFrom } from './index'; export type { SearchParams } from '../registry'; export { getFile } from '../registry'; @@ -130,7 +130,7 @@ export async function getPackageInfo(options: { latestVersion: latestPackage.version, title: packageInfo.title || nameAsTitle(packageInfo.name), assets: Registry.groupPathsByService(paths || []), - removable: !isUnremovablePackage(pkgName), + removable: true, notice: Registry.getNoticePath(paths || []), keepPoliciesUpToDate: savedObject?.attributes.keep_policies_up_to_date ?? false, }; diff --git a/x-pack/plugins/fleet/server/services/epm/packages/index.ts b/x-pack/plugins/fleet/server/services/epm/packages/index.ts index feee4277ab0e1..2c79045c626d3 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/index.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/index.ts @@ -7,11 +7,7 @@ import type { SavedObject } from 'src/core/server'; -import { - unremovablePackages, - installationStatuses, - KibanaSavedObjectType, -} from '../../../../common'; +import { installationStatuses, KibanaSavedObjectType } from '../../../../common'; import { KibanaAssetType } from '../../../types'; import type { AssetType, Installable, Installation } from '../../../types'; @@ -32,10 +28,6 @@ export type { BulkInstallResponse, IBulkInstallPackageError } from './install'; export { handleInstallPackageFailure, installPackage, ensureInstalledPackage } from './install'; export { removeInstallation } from './remove'; -export function isUnremovablePackage(value: string): boolean { - return unremovablePackages.includes(value); -} - export class PackageNotInstalledError extends Error { constructor(pkgkey: string) { super(`${pkgkey} is not installed`); diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index a7e10ba4aa578..a42e71022e052 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -43,7 +43,7 @@ import type { ArchiveAsset } from '../kibana/assets/install'; import type { PackageUpdateEvent } from '../../upgrade_sender'; import { sendTelemetryEvents, UpdateEventType } from '../../upgrade_sender'; -import { isUnremovablePackage, getInstallation, getInstallationObject } from './index'; +import { getInstallation, getInstallationObject } from './index'; import { removeInstallation } from './remove'; import { getPackageSavedObjects } from './get'; import { _installPackage } from './_install_package'; @@ -534,7 +534,6 @@ export async function createInstallation(options: { }) { const { savedObjectsClient, packageInfo, installSource } = options; const { name: pkgName, version: pkgVersion } = packageInfo; - const removable = !isUnremovablePackage(pkgName); const toSaveESIndexPatterns = generateESIndexPatterns(packageInfo.data_streams); // For "stack-aligned" packages, default the `keep_policies_up_to_date` setting to true. For all other @@ -546,6 +545,7 @@ export async function createInstallation(options: { ? true : undefined; + // TODO cleanup removable flag and isUnremovablePackage function const created = await savedObjectsClient.create( PACKAGES_SAVED_OBJECT_TYPE, { @@ -556,7 +556,7 @@ export async function createInstallation(options: { es_index_patterns: toSaveESIndexPatterns, name: pkgName, version: pkgVersion, - removable, + removable: true, install_version: pkgVersion, install_status: 'installing', install_started_at: new Date().toISOString(), diff --git a/x-pack/plugins/fleet/server/services/index.ts b/x-pack/plugins/fleet/server/services/index.ts index 41b7d6c4e6ac8..55769928548e3 100644 --- a/x-pack/plugins/fleet/server/services/index.ts +++ b/x-pack/plugins/fleet/server/services/index.ts @@ -31,7 +31,6 @@ export interface ESIndexPatternService { export interface AgentPolicyServiceInterface { get: typeof agentPolicyService['get']; list: typeof agentPolicyService['list']; - getDefaultAgentPolicyId: typeof agentPolicyService['getDefaultAgentPolicyId']; getFullAgentPolicy: typeof agentPolicyService['getFullAgentPolicy']; getByIds: typeof agentPolicyService['getByIDs']; } diff --git a/x-pack/plugins/fleet/server/services/package_policy.test.ts b/x-pack/plugins/fleet/server/services/package_policy.test.ts index a44355522dd42..51489adcd68f0 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.test.ts @@ -49,6 +49,7 @@ import { updatePackageInputs, packagePolicyService, _applyIndexPrivileges, + incrementPackageName, } from './package_policy'; import { appContextService } from './app_context'; import { fetchInfo } from './epm/registry'; @@ -2891,6 +2892,7 @@ describe('Package policy service', () => { name: 'apache-1', inputs: [{ type: 'logfile', enabled: false }], package: { name: 'apache', version: '0.3.3' }, + policy_id: '1', } as NewPackagePolicy; const result = await packagePolicyService.enrichPolicyWithDefaultsFromPackage( savedObjectsClientMock.create(), @@ -2967,6 +2969,7 @@ describe('Package policy service', () => { name: 'aws-1', inputs: [{ type: 'aws/metrics', policy_template: 'cloudwatch', enabled: true }], package: { name: 'aws', version: '1.0.0' }, + policy_id: '1', } as NewPackagePolicy; const result = await packagePolicyService.enrichPolicyWithDefaultsFromPackage( savedObjectsClientMock.create(), @@ -3239,4 +3242,25 @@ describe('_applyIndexPrivileges()', () => { const streamOut = _applyIndexPrivileges(packageStream, inputStream); expect(streamOut).toEqual(expectedStream); }); + + describe('increment package name', () => { + it('should return 1 if no existing policies', async () => { + packagePolicyService.list = jest.fn().mockResolvedValue(undefined); + const newName = await incrementPackageName(savedObjectsClientMock.create(), 'apache'); + expect(newName).toEqual('apache-1'); + }); + + it('should return 11 if max policy name is 10', async () => { + packagePolicyService.list = jest.fn().mockResolvedValue({ + items: [ + { name: 'apache-1' }, + { name: 'aws-11' }, + { name: 'apache-10' }, + { name: 'apache-9' }, + ], + }); + const newName = await incrementPackageName(savedObjectsClientMock.create(), 'apache'); + expect(newName).toEqual('apache-11'); + }); + }); }); diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index b0ef179834ebb..840642492b262 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -112,7 +112,9 @@ class PackagePolicyService { // Check that the name does not exist already if (existingPoliciesWithName.items.length > 0) { - throw new IngestManagerError('There is already an integration policy with the same name'); + throw new IngestManagerError( + `There is already an integration policy with the same name: ${packagePolicy.name}` + ); } } @@ -730,14 +732,23 @@ class PackagePolicyService { })), } as NewPackagePolicyInput; }); + let agentPolicyId; + // fallback to first agent policy id in case no policy_id is specified, BWC with 8.0 + if (!newPolicy.policy_id) { + const { items: agentPolicies } = await agentPolicyService.list(soClient, { + perPage: 1, + }); + if (agentPolicies.length > 0) { + agentPolicyId = agentPolicies[0].id; + } + } newPackagePolicy = { ...newPP, name: newPolicy.name, namespace: newPolicy.namespace ?? 'default', description: newPolicy.description ?? '', enabled: newPolicy.enabled ?? true, - policy_id: - newPolicy.policy_id ?? (await agentPolicyService.getDefaultAgentPolicyId(soClient)), + policy_id: newPolicy.policy_id ?? agentPolicyId, output_id: newPolicy.output_id ?? '', inputs: newPolicy.inputs[0]?.streams ? newPolicy.inputs : inputs, vars: newPolicy.vars || newPP.vars, @@ -1380,7 +1391,7 @@ function deepMergeVars(original: any, override: any, keepOriginalValue = false): export async function incrementPackageName( soClient: SavedObjectsClientContract, packageName: string -) { +): Promise { // Fetch all packagePolicies having the package name const packagePolicyData = await packagePolicyService.list(soClient, { perPage: SO_SEARCH_LIMIT, @@ -1390,16 +1401,12 @@ export async function incrementPackageName( // Retrieve highest number appended to package policy name and increment it by one const pkgPoliciesNamePattern = new RegExp(`${packageName}-(\\d+)`); - const pkgPoliciesWithMatchingNames = packagePolicyData?.items - ? packagePolicyData.items - .filter((ds) => Boolean(ds.name.match(pkgPoliciesNamePattern))) - .map((ds) => parseInt(ds.name.match(pkgPoliciesNamePattern)![1], 10)) - .sort((a, b) => a - b) - : []; - - return `${packageName}-${ - pkgPoliciesWithMatchingNames.length - ? pkgPoliciesWithMatchingNames[pkgPoliciesWithMatchingNames.length - 1] + 1 - : 1 - }`; + const maxPkgPolicyName = Math.max( + ...(packagePolicyData?.items ?? []) + .filter((ds) => Boolean(ds.name.match(pkgPoliciesNamePattern))) + .map((ds) => parseInt(ds.name.match(pkgPoliciesNamePattern)![1], 10)), + 0 + ); + + return `${packageName}-${maxPkgPolicyName + 1}`; } diff --git a/x-pack/plugins/fleet/server/services/setup.ts b/x-pack/plugins/fleet/server/services/setup.ts index 185be7bffdf54..b99b73be8588c 100644 --- a/x-pack/plugins/fleet/server/services/setup.ts +++ b/x-pack/plugins/fleet/server/services/setup.ts @@ -11,7 +11,8 @@ import type { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/s import { AUTO_UPDATE_PACKAGES } from '../../common'; import type { DefaultPackagesInstallationError, PreconfigurationError } from '../../common'; -import { SO_SEARCH_LIMIT, DEFAULT_PACKAGES } from '../constants'; + +import { SO_SEARCH_LIMIT } from '../constants'; import { DEFAULT_SPACE_ID } from '../../../spaces/common/constants'; import { appContextService } from './app_context'; @@ -26,7 +27,6 @@ import { outputService } from './output'; import { generateEnrollmentAPIKey, hasEnrollementAPIKeysForPolicy } from './api_keys'; import { settingsService } from '.'; import { awaitIfPending } from './setup_utils'; -import { ensureFleetServerAgentPoliciesExists } from './agents'; import { ensureFleetFinalPipelineIsInstalled } from './epm/elasticsearch/ingest_pipeline/install'; import { ensureDefaultComponentTemplate } from './epm/elasticsearch/template/install'; import { getInstallations, installPackage } from './epm/packages'; @@ -93,7 +93,6 @@ async function createSetupSideEffects( packages = [ ...packages, - ...DEFAULT_PACKAGES.filter((pkg) => !preconfiguredPackageNames.has(pkg.name)), ...autoUpdateablePackages.filter((pkg) => !preconfiguredPackageNames.has(pkg.name)), ]; @@ -114,9 +113,6 @@ async function createSetupSideEffects( logger.debug('Setting up Fleet enrollment keys'); await ensureDefaultEnrollmentAPIKeysExists(soClient, esClient); - logger.debug('Setting up Fleet Server agent policies'); - await ensureFleetServerAgentPoliciesExists(soClient, esClient); - if (nonFatalErrors.length > 0) { logger.info('Encountered non fatal errors during Fleet setup'); formatNonFatalErrors(nonFatalErrors).forEach((error) => logger.info(JSON.stringify(error))); 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 840dbd0ccb607..bc89861928cc1 100644 --- a/x-pack/plugins/fleet/server/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/server/types/models/agent_policy.ts @@ -16,6 +16,9 @@ export const AgentPolicyBaseSchema = { namespace: NamespaceSchema, description: schema.maybe(schema.string()), is_managed: schema.maybe(schema.boolean()), + has_fleet_server: schema.maybe(schema.boolean()), + is_default: schema.maybe(schema.boolean()), + is_default_fleet_server: schema.maybe(schema.boolean()), unenroll_timeout: schema.maybe(schema.number({ min: 0 })), monitoring_enabled: schema.maybe( schema.arrayOf( diff --git a/x-pack/plugins/fleet/server/types/models/preconfiguration.ts b/x-pack/plugins/fleet/server/types/models/preconfiguration.ts index 4d030e1e87ed4..960ecbe67d593 100644 --- a/x-pack/plugins/fleet/server/types/models/preconfiguration.ts +++ b/x-pack/plugins/fleet/server/types/models/preconfiguration.ts @@ -8,12 +8,7 @@ import { i18n } from '@kbn/i18n'; import { schema } from '@kbn/config-schema'; import semverValid from 'semver/functions/valid'; -import { - PRECONFIGURATION_LATEST_KEYWORD, - DEFAULT_AGENT_POLICY, - DEFAULT_FLEET_SERVER_AGENT_POLICY, - DEFAULT_PACKAGES, -} from '../../constants'; +import { PRECONFIGURATION_LATEST_KEYWORD } from '../../constants'; import type { PreconfiguredOutput } from '../../../common'; import { outputType } from '../../../common'; @@ -45,7 +40,7 @@ export const PreconfiguredPackagesSchema = schema.arrayOf( }), }), { - defaultValue: DEFAULT_PACKAGES, + defaultValue: [], } ); @@ -103,6 +98,7 @@ export const PreconfiguredAgentPoliciesSchema = schema.arrayOf( id: schema.maybe(schema.oneOf([schema.string(), schema.number()])), is_default: schema.maybe(schema.boolean()), is_default_fleet_server: schema.maybe(schema.boolean()), + has_fleet_server: schema.maybe(schema.boolean()), data_output_id: schema.maybe(schema.string()), monitoring_output_id: schema.maybe(schema.string()), package_policies: schema.arrayOf( @@ -141,6 +137,6 @@ export const PreconfiguredAgentPoliciesSchema = schema.arrayOf( ), }), { - defaultValue: [DEFAULT_AGENT_POLICY, DEFAULT_FLEET_SERVER_AGENT_POLICY], + defaultValue: [], } ); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 351b7f3e1a25b..93631d3fbabd2 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -11174,7 +11174,6 @@ "xpack.fleet.agentPolicyForm.advancedOptionsToggleLabel": "高度なオプション", "xpack.fleet.agentPolicyForm.descriptionFieldLabel": "説明", "xpack.fleet.agentPolicyForm.descriptionFieldPlaceholder": "どのようにこのポリシーを使用しますか?", - "xpack.fleet.agentPolicyForm.monitoringDescription": "パフォーマンスのデバッグと追跡のために、エージェントに関するデータを収集します。監視データは上記のデフォルト名前空間に書き込まれます。", "xpack.fleet.agentPolicyForm.monitoringLabel": "アラート監視", "xpack.fleet.agentPolicyForm.monitoringLogsFieldLabel": "エージェントログを収集", "xpack.fleet.agentPolicyForm.monitoringLogsTooltipText": "このポリシーを使用するElasticエージェントからログを収集します。", @@ -11186,9 +11185,7 @@ "xpack.fleet.agentPolicyForm.namespaceFieldDescription": "名前空間はユーザーが構成できる任意のグループであり、データの検索とユーザーアクセス権の管理が容易になります。ポリシー名前空間は、統合のデータストリームの名前を設定するために使用されます。{fleetUserGuide}。", "xpack.fleet.agentPolicyForm.nameSpaceFieldDescription.fleetUserGuideLabel": "詳細", "xpack.fleet.agentPolicyForm.namespaceFieldLabel": "デフォルト名前空間", - "xpack.fleet.agentPolicyForm.systemMonitoringFieldLabel": "システム監視", "xpack.fleet.agentPolicyForm.systemMonitoringText": "システムログとメトリックの収集", - "xpack.fleet.agentPolicyForm.systemMonitoringTooltipText": "このオプションを有効にすると、システムログとメトリックを収集する統合でポリシーをブートストラップできます。", "xpack.fleet.agentPolicyForm.unenrollmentTimeoutDescription": "任意のタイムアウト(秒)。指定されている場合、この期間が経過した後、エージェントは自動的に登録解除されます。", "xpack.fleet.agentPolicyForm.unenrollmentTimeoutLabel": "登録解除タイムアウト", "xpack.fleet.agentPolicyForm.unenrollTimeoutMinValueErrorMessage": "タイムアウトは0よりも大きい値でなければなりません。", @@ -11294,7 +11291,6 @@ "xpack.fleet.createPackagePolicy.stepConfigure.toggleAdvancedOptionsButtonText": "高度なオプション", "xpack.fleet.createPackagePolicy.stepConfigurePackagePolicyTitle": "統合の構成", "xpack.fleet.createPackagePolicy.stepSelectAgentPolicyTitle": "エージェントポリシーに適用", - "xpack.fleet.createPackagePolicy.StepSelectPolicy.addButton": "エージェントポリシーを作成", "xpack.fleet.createPackagePolicy.StepSelectPolicy.agentPolicyFormGroupDescription": "エージェントポリシーは、エージェントのセットで統合のグループを管理するために使用されます。", "xpack.fleet.createPackagePolicy.StepSelectPolicy.agentPolicyFormGroupTitle": "エージェントポリシー", "xpack.fleet.createPackagePolicy.StepSelectPolicy.agentPolicyLabel": "エージェントポリシー", @@ -11374,7 +11370,6 @@ "xpack.fleet.enrollmentStepAgentPolicy.enrollmentTokenSelectLabel": "登録トークン", "xpack.fleet.enrollmentStepAgentPolicy.noEnrollmentTokensForSelectedPolicyCallout": "選択したエージェントポリシーの登録トークンはありません", "xpack.fleet.enrollmentStepAgentPolicy.policySelectAriaLabel": "エージェントポリシー", - "xpack.fleet.enrollmentStepAgentPolicy.policySelectLabel": "エージェントポリシー", "xpack.fleet.enrollmentStepAgentPolicy.setUpAgentsLink": "登録トークンを作成", "xpack.fleet.enrollmentStepAgentPolicy.showAuthenticationSettingsButton": "認証設定", "xpack.fleet.enrollmentTokenDeleteModal.cancelButton": "キャンセル", @@ -11505,8 +11500,6 @@ "xpack.fleet.fleetServerSetup.addFleetServerHostStepTitle": "Fleetサーバーホストの追加", "xpack.fleet.fleetServerSetup.addFleetServerHostSuccessText": "{host}が追加されました。 {fleetSettingsLink}でFleetサーバーを編集できます。", "xpack.fleet.fleetServerSetup.addFleetServerHostSuccessTitle": "追加されたFleetサーバーホスト", - "xpack.fleet.fleetServerSetup.agentPolicySelectAraiLabel": "エージェントポリシー", - "xpack.fleet.fleetServerSetup.agentPolicySelectLabel": "エージェントポリシー", "xpack.fleet.fleetServerSetup.cloudDeploymentLink": "デプロイを編集", "xpack.fleet.fleetServerSetup.cloudSetupText": "Fleetにエージェントを登録する前に、Fleetサーバーが必要です。APM & Fleetを有効にしてデプロイに追加できます。詳細は{link}をご覧ください。", "xpack.fleet.fleetServerSetup.cloudSetupTitle": "APM & Fleetを有効にする", @@ -11524,7 +11517,6 @@ "xpack.fleet.fleetServerSetup.productionText": "本番運用", "xpack.fleet.fleetServerSetup.quickStartText": "クイックスタート", "xpack.fleet.fleetServerSetup.saveServiceTokenDescription": "サービストークン情報を保存します。これは1回だけ表示されます。", - "xpack.fleet.fleetServerSetup.selectAgentPolicyDescriptionText": "エージェントポリシーを使用すると、リモートでエージェントを構成および管理できます。Fleetサーバーを実行するために必要な構成を含む「デフォルトFleetサーバーポリシー」を使用することをお勧めします。", "xpack.fleet.fleetServerSetup.serviceTokenLabel": "サービストークン", "xpack.fleet.fleetServerSetup.setupGuideLink": "FleetおよびElasticエージェントガイド", "xpack.fleet.fleetServerSetup.setupText": "Fleetにエージェントを登録する前に、Fleetサーバーが必要です。Fleetサーバーのセットアップについては、次の手順に従ってください。詳細については、{userGuideLink}を参照してください。", @@ -11692,7 +11684,6 @@ "xpack.fleet.policyForm.deletePolicyGroupTitle": "ポリシーを削除", "xpack.fleet.policyForm.generalSettingsGroupDescription": "エージェントポリシーの名前と説明を選択してください。", "xpack.fleet.policyForm.generalSettingsGroupTitle": "一般設定", - "xpack.fleet.policyForm.unableToDeleteDefaultPolicyText": "デフォルトポリシーは削除できません", "xpack.fleet.preconfiguration.duplicatePackageError": "構成で重複するパッケージが指定されています。{duplicateList}", "xpack.fleet.preconfiguration.missingIDError": "{agentPolicyName}には「id」フィールドがありません。ポリシーのis_defaultまたはis_default_fleet_serverに設定されている場合をのぞき、「id」は必須です。", "xpack.fleet.preconfiguration.packageMissingError": "[{agentPolicyName}]を追加できませんでした。[{pkgName}]がインストールされていません。[{pkgName}]を[{packagesConfigValue}]に追加するか、[{packagePolicyName}]から削除してください。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 1bc91bc8b12b7..9e89f9a63f961 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -11285,7 +11285,6 @@ "xpack.fleet.agentPolicyForm.advancedOptionsToggleLabel": "高级选项", "xpack.fleet.agentPolicyForm.descriptionFieldLabel": "描述", "xpack.fleet.agentPolicyForm.descriptionFieldPlaceholder": "此策略将如何使用?", - "xpack.fleet.agentPolicyForm.monitoringDescription": "收集有关代理的数据,用于调试和跟踪性能。监测数据将写入到上面指定的默认命名空间。", "xpack.fleet.agentPolicyForm.monitoringLabel": "代理监测", "xpack.fleet.agentPolicyForm.monitoringLogsFieldLabel": "收集代理日志", "xpack.fleet.agentPolicyForm.monitoringLogsTooltipText": "从使用此策略的 Elastic 代理收集日志。", @@ -11297,9 +11296,7 @@ "xpack.fleet.agentPolicyForm.namespaceFieldDescription": "命名空间是用户可配置的任意分组,使搜索数据和管理用户权限更容易。策略命名空间用于命名其集成的数据流。{fleetUserGuide}。", "xpack.fleet.agentPolicyForm.nameSpaceFieldDescription.fleetUserGuideLabel": "了解详情", "xpack.fleet.agentPolicyForm.namespaceFieldLabel": "默认命名空间", - "xpack.fleet.agentPolicyForm.systemMonitoringFieldLabel": "系统监测", "xpack.fleet.agentPolicyForm.systemMonitoringText": "收集系统日志和指标", - "xpack.fleet.agentPolicyForm.systemMonitoringTooltipText": "启用此选项可使用收集系统日志和指标的集成启动您的策略。", "xpack.fleet.agentPolicyForm.unenrollmentTimeoutDescription": "可选超时(秒)。若提供,代理断开连接此段时间后,将自动注销。", "xpack.fleet.agentPolicyForm.unenrollmentTimeoutLabel": "注销超时", "xpack.fleet.agentPolicyForm.unenrollTimeoutMinValueErrorMessage": "超时必须大于零。", @@ -11408,7 +11405,6 @@ "xpack.fleet.createPackagePolicy.stepConfigure.toggleAdvancedOptionsButtonText": "高级选项", "xpack.fleet.createPackagePolicy.stepConfigurePackagePolicyTitle": "配置集成", "xpack.fleet.createPackagePolicy.stepSelectAgentPolicyTitle": "应用到代理策略", - "xpack.fleet.createPackagePolicy.StepSelectPolicy.addButton": "创建代理策略", "xpack.fleet.createPackagePolicy.StepSelectPolicy.agentPolicyAgentsDescriptionText": "{count, plural, other {# 个代理}}已注册到选定代理策略。", "xpack.fleet.createPackagePolicy.StepSelectPolicy.agentPolicyFormGroupDescription": "代理策略用于跨代理集管理一组集成。", "xpack.fleet.createPackagePolicy.StepSelectPolicy.agentPolicyFormGroupTitle": "代理策略", @@ -11493,7 +11489,6 @@ "xpack.fleet.enrollmentStepAgentPolicy.enrollmentTokenSelectLabel": "注册令牌", "xpack.fleet.enrollmentStepAgentPolicy.noEnrollmentTokensForSelectedPolicyCallout": "选定的代理策略没有注册令牌", "xpack.fleet.enrollmentStepAgentPolicy.policySelectAriaLabel": "代理策略", - "xpack.fleet.enrollmentStepAgentPolicy.policySelectLabel": "代理策略", "xpack.fleet.enrollmentStepAgentPolicy.setUpAgentsLink": "创建注册令牌", "xpack.fleet.enrollmentStepAgentPolicy.showAuthenticationSettingsButton": "身份验证设置", "xpack.fleet.enrollmentTokenDeleteModal.cancelButton": "取消", @@ -11624,8 +11619,6 @@ "xpack.fleet.fleetServerSetup.addFleetServerHostStepTitle": "添加您的 Fleet 服务器主机", "xpack.fleet.fleetServerSetup.addFleetServerHostSuccessText": "已添加 {host}。您可以在{fleetSettingsLink}中编辑 Fleet 服务器主机。", "xpack.fleet.fleetServerSetup.addFleetServerHostSuccessTitle": "已添加 Fleet 服务器主机", - "xpack.fleet.fleetServerSetup.agentPolicySelectAraiLabel": "代理策略", - "xpack.fleet.fleetServerSetup.agentPolicySelectLabel": "代理策略", "xpack.fleet.fleetServerSetup.cloudDeploymentLink": "编辑部署", "xpack.fleet.fleetServerSetup.cloudSetupText": "需要提供 Fleet 服务器,才能使用 Fleet 注册代理。可以通过启用 APM & Fleet 来将一个添加到部署中。有关更多信息,请参阅{link}", "xpack.fleet.fleetServerSetup.cloudSetupTitle": "启用 APM & Fleet", @@ -11643,7 +11636,6 @@ "xpack.fleet.fleetServerSetup.productionText": "生产", "xpack.fleet.fleetServerSetup.quickStartText": "快速启动", "xpack.fleet.fleetServerSetup.saveServiceTokenDescription": "保存服务令牌信息。其仅显示一次。", - "xpack.fleet.fleetServerSetup.selectAgentPolicyDescriptionText": "代理策略允许您远程配置和管理您的代理。建议使用“默认 Fleet 服务器策略”,其包含运行 Fleet 服务器的必要配置。", "xpack.fleet.fleetServerSetup.serviceTokenLabel": "服务令牌", "xpack.fleet.fleetServerSetup.setupGuideLink": "Fleet 和 Elastic 代理指南", "xpack.fleet.fleetServerSetup.setupText": "需要提供 Fleet 服务器,才能使用 Fleet 注册代理。按照下面的说明设置 Fleet 服务器。有关详细信息,请参阅{userGuideLink}。", @@ -11816,7 +11808,6 @@ "xpack.fleet.policyForm.deletePolicyGroupTitle": "删除策略", "xpack.fleet.policyForm.generalSettingsGroupDescription": "为您的代理策略选择名称和描述。", "xpack.fleet.policyForm.generalSettingsGroupTitle": "常规设置", - "xpack.fleet.policyForm.unableToDeleteDefaultPolicyText": "默认策略无法删除", "xpack.fleet.preconfiguration.duplicatePackageError": "配置中指定的软件包重复:{duplicateList}", "xpack.fleet.preconfiguration.missingIDError": "{agentPolicyName} 缺失 `id` 字段。`id` 是必需的,但标记为 is_default 或 is_default_fleet_server 的策略除外。", "xpack.fleet.preconfiguration.packageMissingError": "无法添加 [{agentPolicyName}]。[{pkgName}] 未安装,请将 [{pkgName}] 添加到 [{packagesConfigValue}] 或将其从 [{packagePolicyName}] 中移除。", diff --git a/x-pack/test/fleet_api_integration/apis/enrollment_api_keys/crud.ts b/x-pack/test/fleet_api_integration/apis/enrollment_api_keys/crud.ts index a628add1de838..d03b77153fa6b 100644 --- a/x-pack/test/fleet_api_integration/apis/enrollment_api_keys/crud.ts +++ b/x-pack/test/fleet_api_integration/apis/enrollment_api_keys/crud.ts @@ -37,9 +37,9 @@ export default function (providerContext: FtrProviderContext) { .get(`/api/fleet/enrollment_api_keys`) .expect(200); - expect(apiResponse.total).to.be(3); + expect(apiResponse.total).to.be(2); expect(apiResponse.items[0]).to.have.keys('id', 'api_key_id', 'name'); - expect(apiResponse).to.have.keys('list'); + expect(apiResponse).to.have.keys('items'); }); }); diff --git a/x-pack/test/fleet_api_integration/apis/epm/delete.ts b/x-pack/test/fleet_api_integration/apis/epm/delete.ts index 5e839e51113c4..6e988c6f0d0f7 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/delete.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/delete.ts @@ -41,14 +41,14 @@ export default function (providerContext: FtrProviderContext) { await deletePackage(requiredPackage, pkgVersion); }); - it('should return 400 if trying to uninstall a required package', async function () { + it('should return 200 if trying to uninstall a required package', async function () { await supertest .delete(`/api/fleet/epm/packages/${requiredPackage}/${pkgVersion}`) .set('kbn-xsrf', 'xxxx') - .expect(400); + .expect(200); }); - it('should return 200 if trying to force uninstall a required package', async function () { + it.skip('should return 200 if trying to force uninstall a required package', async function () { await supertest .delete(`/api/fleet/epm/packages/${requiredPackage}/${pkgVersion}`) .set('kbn-xsrf', 'xxxx') diff --git a/x-pack/test/fleet_api_integration/apis/epm/setup.ts b/x-pack/test/fleet_api_integration/apis/epm/setup.ts index b4812b39f74ef..1e828cf63cfd5 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/setup.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/setup.ts @@ -82,12 +82,25 @@ export default function (providerContext: FtrProviderContext) { .set('Authorization', `Bearer ${token.value}`) .set('kbn-xsrf', 'xxx') .expect(200); - const response = await supertestWithoutAuth + const resp = await supertest + .post('/api/fleet/agent_policies') + .set('Authorization', `Bearer ${token.value}`) + .set('kbn-xsrf', 'xxx') + .send({ name: 'Agent policy', namespace: 'default' }) + .expect(200); + const agentPolicyId = resp.body.item.id; + await supertestWithoutAuth .get('/api/fleet/enrollment_api_keys') .set('Authorization', `Bearer ${token.value}`) .set('kbn-xsrf', 'xxx') .expect(200); - const enrollmentApiKeyId = response.body.items[0].id; + const response = await supertest + .post('/api/fleet/enrollment_api_keys') + .set('Authorization', `Bearer ${token.value}`) + .set('kbn-xsrf', 'xxx') + .send({ policy_id: agentPolicyId }) + .expect(200); + const enrollmentApiKeyId = response.body.item.id; await supertestWithoutAuth .get(`/api/fleet/enrollment_api_keys/${enrollmentApiKeyId}`) .set('Authorization', `Bearer ${token.value}`) diff --git a/x-pack/test/fleet_api_integration/apis/fleet_setup.ts b/x-pack/test/fleet_api_integration/apis/fleet_setup.ts index c399f53fe8366..1d640f50c04a3 100644 --- a/x-pack/test/fleet_api_integration/apis/fleet_setup.ts +++ b/x-pack/test/fleet_api_integration/apis/fleet_setup.ts @@ -75,7 +75,7 @@ export default function (providerContext: FtrProviderContext) { .map((p: any) => p.name) .sort(); - expect(installedPackages).to.eql(['elastic_agent', 'endpoint', 'fleet_server', 'system']); + expect(installedPackages).to.eql(['endpoint']); }); }); } diff --git a/x-pack/test/fleet_api_integration/apis/fleet_telemetry.ts b/x-pack/test/fleet_api_integration/apis/fleet_telemetry.ts index 4d06de83f6917..16350114358c4 100644 --- a/x-pack/test/fleet_api_integration/apis/fleet_telemetry.ts +++ b/x-pack/test/fleet_api_integration/apis/fleet_telemetry.ts @@ -70,20 +70,35 @@ export default function (providerContext: FtrProviderContext) { }); before(async () => { - // Get FleetServer policy id - const { body: apiResponse } = await supertest.get(`/api/fleet/agent_policies`).expect(200); - const defaultFleetServerPolicy = apiResponse.items.find( - (item: any) => item.is_default_fleet_server - ); + // create agent policies + let { body: apiResponse } = await supertest + .post(`/api/fleet/agent_policies`) + .set('kbn-xsrf', 'kibana') + .send({ + name: 'Fleet Server policy 1', + namespace: 'default', + has_fleet_server: true, + }) + .expect(200); + const fleetServerPolicy = apiResponse.item; + + ({ body: apiResponse } = await supertest + .post(`/api/fleet/agent_policies`) + .set('kbn-xsrf', 'kibana') + .send({ + name: 'Agent policy 1', + namespace: 'default', + }) + .expect(200)); - const defaultServerPolicy = apiResponse.items.find((item: any) => item.is_default); + const agentPolicy = apiResponse.item; - if (!defaultFleetServerPolicy) { - throw new Error('No default Fleet server policy'); + if (!fleetServerPolicy) { + throw new Error('No Fleet server policy'); } - if (!defaultServerPolicy) { - throw new Error('No default policy'); + if (!agentPolicy) { + throw new Error('No agent policy'); } await supertest @@ -93,16 +108,16 @@ export default function (providerContext: FtrProviderContext) { .expect(200); // Default Fleet Server - await generateAgent('healthy', defaultFleetServerPolicy.id); - await generateAgent('healthy', defaultFleetServerPolicy.id); - await generateAgent('error', defaultFleetServerPolicy.id); + await generateAgent('healthy', fleetServerPolicy.id); + await generateAgent('healthy', fleetServerPolicy.id); + await generateAgent('error', fleetServerPolicy.id); // Default policy - await generateAgent('healthy', defaultServerPolicy.id); - await generateAgent('offline', defaultServerPolicy.id); - await generateAgent('error', defaultServerPolicy.id); - await generateAgent('degraded', defaultServerPolicy.id); - await generateAgent('error-unenrolling', defaultServerPolicy.id); + await generateAgent('healthy', agentPolicy.id); + await generateAgent('offline', agentPolicy.id); + await generateAgent('error', agentPolicy.id); + await generateAgent('degraded', agentPolicy.id); + await generateAgent('error-unenrolling', agentPolicy.id); }); it('should return the correct telemetry values for fleet', async () => { diff --git a/x-pack/test/fleet_cypress/agent.ts b/x-pack/test/fleet_cypress/agent.ts index 1838d9df8b3e2..c0e5b2374889d 100644 --- a/x-pack/test/fleet_cypress/agent.ts +++ b/x-pack/test/fleet_cypress/agent.ts @@ -11,11 +11,12 @@ import { ChildProcess, spawn } from 'child_process'; import { getLatestVersion } from './artifact_manager'; import { Manager } from './resource_manager'; -interface AgentManagerParams { +export interface AgentManagerParams { user: string; password: string; kibanaUrl: string; esHost: string; + esPort: string; } export class AgentManager extends Manager { @@ -23,19 +24,11 @@ export class AgentManager extends Manager { private log: ToolingLog; private agentProcess?: ChildProcess; private requestOptions: AxiosRequestConfig; - constructor(params: AgentManagerParams, log: ToolingLog) { + constructor(params: AgentManagerParams, log: ToolingLog, requestOptions: AxiosRequestConfig) { super(); this.log = log; this.params = params; - this.requestOptions = { - headers: { - 'kbn-xsrf': 'kibana', - }, - auth: { - username: this.params.user, - password: this.params.password, - }, - }; + this.requestOptions = requestOptions; } public async setup() { diff --git a/x-pack/test/fleet_cypress/artifact_manager.ts b/x-pack/test/fleet_cypress/artifact_manager.ts index aea0eb8bbec86..17ba9b0a5517d 100644 --- a/x-pack/test/fleet_cypress/artifact_manager.ts +++ b/x-pack/test/fleet_cypress/artifact_manager.ts @@ -10,5 +10,5 @@ import { last } from 'lodash'; export async function getLatestVersion(): Promise { const response: any = await axios('https://artifacts-api.elastic.co/v1/versions'); - return last(response.data.versions as string[]) || '8.0.0-SNAPSHOT'; + return last(response.data.versions as string[]) || '8.1.0-SNAPSHOT'; } diff --git a/x-pack/test/fleet_cypress/fleet_server.ts b/x-pack/test/fleet_cypress/fleet_server.ts index fe2b8c7459229..d3bb4a385185d 100644 --- a/x-pack/test/fleet_cypress/fleet_server.ts +++ b/x-pack/test/fleet_cypress/fleet_server.ts @@ -7,37 +7,52 @@ import { ChildProcess, spawn } from 'child_process'; import { ToolingLog } from '@kbn/dev-utils'; -import axios from 'axios'; +import axios, { AxiosRequestConfig } from 'axios'; import { Manager } from './resource_manager'; import { getLatestVersion } from './artifact_manager'; - -export interface ElasticsearchConfig { - esHost: string; - user: string; - password: string; - port: string; -} +import { AgentManagerParams } from './agent'; export class FleetManager extends Manager { private fleetProcess?: ChildProcess; - private esConfig: ElasticsearchConfig; + private config: AgentManagerParams; private log: ToolingLog; - constructor(esConfig: ElasticsearchConfig, log: ToolingLog) { + private requestOptions: AxiosRequestConfig; + constructor(config: AgentManagerParams, log: ToolingLog, requestOptions: AxiosRequestConfig) { super(); - this.esConfig = esConfig; + this.config = config; this.log = log; + this.requestOptions = requestOptions; } public async setup(): Promise { this.log.info('Setting fleet up'); return new Promise(async (res, rej) => { try { const response = await axios.post( - `${this.esConfig.esHost}/_security/service/elastic/fleet-server/credential/token` + `${this.config.kibanaUrl}/api/fleet/service_tokens`, + {}, + this.requestOptions ); - const serviceToken = response.data.token.value; + const serviceToken = response.data.value; const artifact = `docker.elastic.co/beats/elastic-agent:${await getLatestVersion()}`; this.log.info(artifact); + // default fleet server policy no longer created by default + const { + data: { + item: { id: policyId }, + }, + } = await axios.post( + `${this.config.kibanaUrl}/api/fleet/agent_policies`, + { + name: 'Fleet Server policy', + description: '', + namespace: 'default', + monitoring_enabled: [], + has_fleet_server: true, + }, + this.requestOptions + ); + const host = 'host.docker.internal'; const args = [ @@ -49,12 +64,15 @@ export class FleetManager extends Manager { '--env', 'FLEET_SERVER_ENABLE=true', '--env', - `FLEET_SERVER_ELASTICSEARCH_HOST=http://${host}:${this.esConfig.port}`, + `FLEET_SERVER_ELASTICSEARCH_HOST=http://${host}:${this.config.esPort}`, '--env', `FLEET_SERVER_SERVICE_TOKEN=${serviceToken}`, + '--env', + `FLEET_SERVER_POLICY=${policyId}`, '--rm', artifact, ]; + this.log.info('docker ' + args.join(' ')); this.fleetProcess = spawn('docker', args, { stdio: 'inherit', }); diff --git a/x-pack/test/fleet_cypress/runner.ts b/x-pack/test/fleet_cypress/runner.ts index b49bfbdc091e2..bcd54fa568368 100644 --- a/x-pack/test/fleet_cypress/runner.ts +++ b/x-pack/test/fleet_cypress/runner.ts @@ -12,36 +12,43 @@ import { withProcRunner } from '@kbn/dev-utils'; import { FtrProviderContext } from './ftr_provider_context'; -import { AgentManager } from './agent'; +import { AgentManager, AgentManagerParams } from './agent'; import { FleetManager } from './fleet_server'; async function withFleetAgent( { getService }: FtrProviderContext, runner: (runnerEnv: Record) => Promise ) { + // skipping fleet server enroll for now, as it is not a functionality of Fleet UI itself. are there any existing e2e tests for enroll? + return await runner({}); + const log = getService('log'); const config = getService('config'); const esHost = Url.format(config.get('servers.elasticsearch')); - const esConfig = { + const params: AgentManagerParams = { user: config.get('servers.elasticsearch.username'), password: config.get('servers.elasticsearch.password'), esHost, - port: config.get('servers.elasticsearch.port'), + esPort: config.get('servers.elasticsearch.port'), + kibanaUrl: Url.format({ + protocol: config.get('servers.kibana.protocol'), + hostname: config.get('servers.kibana.hostname'), + port: config.get('servers.kibana.port'), + }), }; - const fleetManager = new FleetManager(esConfig, log); - - const agentManager = new AgentManager( - { - ...esConfig, - kibanaUrl: Url.format({ - protocol: config.get('servers.kibana.protocol'), - hostname: config.get('servers.kibana.hostname'), - port: config.get('servers.kibana.port'), - }), + const requestOptions = { + headers: { + 'kbn-xsrf': 'kibana', }, - log - ); + auth: { + username: params.user, + password: params.password, + }, + }; + const fleetManager = new FleetManager(params, log, requestOptions); + + const agentManager = new AgentManager(params, log, requestOptions); // Since the managers will create uncaughtException event handlers we need to exit manually process.on('uncaughtException', (err) => {