From b494716bdb5b5ef3171f65fd227543df2de517bf Mon Sep 17 00:00:00 2001 From: Dominique Clarke Date: Fri, 21 Apr 2023 09:55:38 -0400 Subject: [PATCH] [Fleet] Create Synthetics migration for 8.8.0 (#154952) ## Summary Resolves https://github.com/elastic/kibana/issues/155215 Resolves https://github.com/elastic/kibana/issues/142653 Handles two primary migrations for Synthetics integration policies. 1. Rounds deprecated schedules to supported schedules 2. Transforms old deprecated throttling schema `5u/3u/20l` to support JSON schema `{ download: 5, upload: 3, latency: 20 }` Schedule migration --- Before 16m schedule Screen Shot 2023-04-19 at 6 17 35 PM After Screen Shot 2023-04-19 at 6 44 08 PM Before 4m schedule Screen Shot 2023-04-19 at 6 17 29 PM After Screen Shot 2023-04-19 at 6 44 30 PM Before 8m schedule Screen Shot 2023-04-19 at 6 17 23 PM After Screen Shot 2023-04-19 at 6 44 22 PM Before 2m schedule Screen Shot 2023-04-19 at 6 17 16 PM After Screen Shot 2023-04-19 at 6 43 55 PM Throttling migration --- Before throttling: false Screen Shot 2023-04-19 at 6 17 00 PM After Screen Shot 2023-04-19 at 6 49 50 PM Before custom throttling Screen Shot 2023-04-19 at 6 16 54 PM After Screen Shot 2023-04-19 at 6 49 44 PM Before default throttling Screen Shot 2023-04-19 at 6 16 48 PM After Screen Shot 2023-04-19 at 6 49 35 PM ### Testing 1. Check out the 8.7.0 branch 2. Create a Synthetics private location at `app/synthetics/settings/private-locations`. 3. Create a monitor, configured at that private location, with invalid schedules, ideally 1 or each type (http, icmp, browser, tcp) with an invalid schedule (for example, 2, 8, 11, 16, 333, etc) at `app/uptime/add-monitor`. Note: you must use Uptime to create monitors with arbitrary schedules. The Synthetics app will not let you. 4. Create a browser monitor, configured with your private location, with throttling turned off. 5. Create a browser monitor, configured for your private location, with a custom throttling profile. 6. Check out this PR and wait for saved object migration to run 7. Navigate to the agent policy for your monitor. Confirm the schedules were updated to supported schedules. Confirm the throttling configs now appear in yaml. Confirm that `throttling: false` remains for the throttling config that was turned off. --- .../fleet/server/saved_objects/index.ts | 6 +- .../migrations/synthetics/fixtures/8.7.0.ts | 1100 +++++++++++++++++ .../migrations/synthetics/index.ts | 8 + .../migrations/synthetics/to_v8_8_0.test.ts | 167 +++ .../migrations/synthetics/to_v8_8_0.ts | 107 ++ .../saved_objects/migrations/to_v8_8_0.ts | 35 + .../server/types/models/package_policy.ts | 1 + .../common/constants/monitor_defaults.ts | 4 +- .../formatters/browser/formatters.test.ts | 70 ++ .../common/formatters/browser/formatters.ts | 7 +- .../format_synthetics_policy.test.ts | 2 +- .../synthetics/services/add_monitor.ts | 14 +- .../synthetics/e2e/tasks/import_monitors.ts | 14 +- .../status_rule/status_rule_executor.test.ts | 14 +- .../synthetics_private_location.test.ts | 2 +- .../sample_data/test_browser_policy.ts | 7 +- .../test_project_monitor_policy.ts | 7 +- 17 files changed, 1538 insertions(+), 27 deletions(-) create mode 100644 x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/fixtures/8.7.0.ts create mode 100644 x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/index.ts create mode 100644 x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/to_v8_8_0.test.ts create mode 100644 x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/to_v8_8_0.ts create mode 100644 x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_8_0.ts create mode 100644 x-pack/plugins/synthetics/common/formatters/browser/formatters.test.ts diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 763c6d6c691364..01d7bc1040368e 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -54,10 +54,8 @@ import { migrateInstallationToV860, migratePackagePolicyToV860, } from './migrations/to_v8_6_0'; -import { - migratePackagePolicyToV870, - migratePackagePolicyToV880, -} from './migrations/security_solution'; +import { migratePackagePolicyToV870 } from './migrations/security_solution'; +import { migratePackagePolicyToV880 } from './migrations/to_v8_8_0'; /* * Saved object types and mappings diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/fixtures/8.7.0.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/fixtures/8.7.0.ts new file mode 100644 index 00000000000000..2c246781840941 --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/fixtures/8.7.0.ts @@ -0,0 +1,1100 @@ +/* + * 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 { SavedObjectUnsanitizedDoc } from '@kbn/core/server'; + +import type { PackagePolicy } from '../../../../../common'; + +export const httpPolicy = { + type: 'ingest-package-policies', + id: 'ce2f0cc6-b082-4080-9ed3-82f38743a3ed-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + attributes: { + name: 'Invalid http monitor with 4 minute schedule-A private location-default', + namespace: 'default', + package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.11.8' }, + enabled: true, + policy_id: 'fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1', + inputs: [ + { + type: 'synthetics/http', + policy_template: 'synthetics', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'http', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + vars: { + __ui: { + value: '{"is_tls_enabled":false,"is_zip_url_tls_enabled":false}', + type: 'yaml', + }, + enabled: { value: true, type: 'bool' }, + type: { value: 'http', type: 'text' }, + name: { value: 'Invalid http monitor with 4 minute schedule', type: 'text' }, + schedule: { value: '"@every 4m"', type: 'text' }, + urls: { value: 'https://elastic.co', type: 'text' }, + 'service.name': { value: '', type: 'text' }, + timeout: { value: '16s', type: 'text' }, + max_redirects: { value: '0', type: 'integer' }, + proxy_url: { value: '', type: 'text' }, + tags: { value: null, type: 'yaml' }, + username: { value: '', type: 'text' }, + password: { value: '', type: 'password' }, + 'response.include_headers': { value: true, type: 'bool' }, + 'response.include_body': { value: 'on_error', type: 'text' }, + 'check.request.method': { value: 'GET', type: 'text' }, + 'check.request.headers': { value: null, type: 'yaml' }, + 'check.request.body': { value: null, type: 'yaml' }, + 'check.response.status': { value: null, type: 'yaml' }, + 'check.response.headers': { value: null, type: 'yaml' }, + 'check.response.body.positive': { value: null, type: 'yaml' }, + 'check.response.body.negative': { value: null, type: 'yaml' }, + 'ssl.certificate_authorities': { value: null, type: 'yaml' }, + 'ssl.certificate': { value: null, type: 'yaml' }, + 'ssl.key': { value: null, type: 'yaml' }, + 'ssl.key_passphrase': { value: null, type: 'text' }, + 'ssl.verification_mode': { value: null, type: 'text' }, + 'ssl.supported_protocols': { value: null, type: 'yaml' }, + location_name: { value: 'A private location', type: 'text' }, + id: { value: 'ce2f0cc6-b082-4080-9ed3-82f38743a3ed', type: 'text' }, + config_id: { value: 'ce2f0cc6-b082-4080-9ed3-82f38743a3ed', type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { value: 'ui', type: 'text' }, + 'monitor.project.id': { value: null, type: 'text' }, + 'monitor.project.name': { value: null, type: 'text' }, + }, + id: 'synthetics/http-http-ce2f0cc6-b082-4080-9ed3-82f38743a3ed-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + __ui: { is_tls_enabled: false, is_zip_url_tls_enabled: false }, + type: 'http', + name: 'Invalid http monitor with 4 minute schedule', + id: 'ce2f0cc6-b082-4080-9ed3-82f38743a3ed', + origin: 'ui', + 'run_from.id': 'A private location', + 'run_from.geo.name': 'A private location', + enabled: true, + urls: 'https://elastic.co', + schedule: '@every 4m', + timeout: '16s', + max_redirects: 0, + 'response.include_headers': true, + 'response.include_body': 'on_error', + 'check.request.method': 'GET', + processors: [ + { + add_fields: { + target: '', + fields: { + 'monitor.fleet_managed': true, + config_id: 'ce2f0cc6-b082-4080-9ed3-82f38743a3ed', + }, + }, + }, + ], + }, + }, + ], + }, + { + type: 'synthetics/tcp', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'tcp' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'tcp', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + hosts: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + proxy_url: { type: 'text' }, + proxy_use_local_resolver: { value: false, type: 'bool' }, + tags: { type: 'yaml' }, + 'check.send': { type: 'text' }, + 'check.receive': { type: 'text' }, + 'ssl.certificate_authorities': { type: 'yaml' }, + 'ssl.certificate': { type: 'yaml' }, + 'ssl.key': { type: 'yaml' }, + 'ssl.key_passphrase': { type: 'text' }, + 'ssl.verification_mode': { type: 'text' }, + 'ssl.supported_protocols': { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/tcp-tcp-ce2f0cc6-b082-4080-9ed3-82f38743a3ed-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + }, + ], + }, + { + type: 'synthetics/icmp', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'icmp' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'icmp', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + wait: { value: '1s', type: 'text' }, + hosts: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + tags: { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/icmp-icmp-ce2f0cc6-b082-4080-9ed3-82f38743a3ed-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + }, + ], + }, + { + type: 'synthetics/browser', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'browser', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + tags: { type: 'yaml' }, + 'source.zip_url.url': { type: 'text' }, + 'source.zip_url.username': { type: 'text' }, + 'source.zip_url.folder': { type: 'text' }, + 'source.zip_url.password': { type: 'password' }, + 'source.inline.script': { type: 'yaml' }, + 'source.project.content': { type: 'text' }, + params: { type: 'yaml' }, + playwright_options: { type: 'yaml' }, + screenshots: { type: 'text' }, + synthetics_args: { type: 'text' }, + ignore_https_errors: { type: 'bool' }, + 'throttling.config': { type: 'text' }, + 'filter_journeys.tags': { type: 'yaml' }, + 'filter_journeys.match': { type: 'text' }, + 'source.zip_url.ssl.certificate_authorities': { type: 'yaml' }, + 'source.zip_url.ssl.certificate': { type: 'yaml' }, + 'source.zip_url.ssl.key': { type: 'yaml' }, + 'source.zip_url.ssl.key_passphrase': { type: 'text' }, + 'source.zip_url.ssl.verification_mode': { type: 'text' }, + 'source.zip_url.ssl.supported_protocols': { type: 'yaml' }, + 'source.zip_url.proxy_url': { type: 'text' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/browser-browser-ce2f0cc6-b082-4080-9ed3-82f38743a3ed-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + __ui: null, + type: 'browser', + name: null, + 'run_from.id': 'Fleet managed', + 'run_from.geo.name': 'Fleet managed', + enabled: true, + schedule: '@every 3m', + timeout: null, + throttling: null, + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.network', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + id: 'synthetics/browser-browser.network-ce2f0cc6-b082-4080-9ed3-82f38743a3ed-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.screenshot', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + id: 'synthetics/browser-browser.screenshot-ce2f0cc6-b082-4080-9ed3-82f38743a3ed-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + ], + }, + ], + is_managed: true, + revision: 1, + created_at: '2023-04-19T15:34:51.655Z', + created_by: 'system', + updated_at: '2023-04-19T15:34:51.655Z', + updated_by: 'system', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-04-19T15:34:51.714Z', + created_at: '2023-04-19T15:34:51.714Z', + typeMigrationVersion: '8.7.0', +} as unknown as SavedObjectUnsanitizedDoc; +export const tcpPolicy = { + type: 'ingest-package-policies', + id: '77f25200-7cf3-450d-abfa-4f95faae1907-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + attributes: { + name: 'Invalid tcp monitor with 8 minute schedule-A private location-default', + namespace: 'default', + package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.11.8' }, + enabled: true, + policy_id: 'fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1', + inputs: [ + { + type: 'synthetics/http', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'http' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'http', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + urls: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + max_redirects: { type: 'integer' }, + proxy_url: { type: 'text' }, + tags: { type: 'yaml' }, + username: { type: 'text' }, + password: { type: 'password' }, + 'response.include_headers': { type: 'bool' }, + 'response.include_body': { type: 'text' }, + 'check.request.method': { type: 'text' }, + 'check.request.headers': { type: 'yaml' }, + 'check.request.body': { type: 'yaml' }, + 'check.response.status': { type: 'yaml' }, + 'check.response.headers': { type: 'yaml' }, + 'check.response.body.positive': { type: 'yaml' }, + 'check.response.body.negative': { type: 'yaml' }, + 'ssl.certificate_authorities': { type: 'yaml' }, + 'ssl.certificate': { type: 'yaml' }, + 'ssl.key': { type: 'yaml' }, + 'ssl.key_passphrase': { type: 'text' }, + 'ssl.verification_mode': { type: 'text' }, + 'ssl.supported_protocols': { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/http-http-77f25200-7cf3-450d-abfa-4f95faae1907-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + }, + ], + }, + { + type: 'synthetics/tcp', + policy_template: 'synthetics', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'tcp', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + vars: { + __ui: { + value: '{"is_tls_enabled":false,"is_zip_url_tls_enabled":false}', + type: 'yaml', + }, + enabled: { value: true, type: 'bool' }, + type: { value: 'tcp', type: 'text' }, + name: { value: 'Invalid tcp monitor with 8 minute schedule', type: 'text' }, + schedule: { value: '"@every 8m"', type: 'text' }, + hosts: { value: 'localhost:5601', type: 'text' }, + 'service.name': { value: '', type: 'text' }, + timeout: { value: '16s', type: 'text' }, + proxy_url: { value: '', type: 'text' }, + proxy_use_local_resolver: { value: false, type: 'bool' }, + tags: { value: null, type: 'yaml' }, + 'check.send': { value: '', type: 'text' }, + 'check.receive': { value: '', type: 'text' }, + 'ssl.certificate_authorities': { value: null, type: 'yaml' }, + 'ssl.certificate': { value: null, type: 'yaml' }, + 'ssl.key': { value: null, type: 'yaml' }, + 'ssl.key_passphrase': { value: null, type: 'text' }, + 'ssl.verification_mode': { value: null, type: 'text' }, + 'ssl.supported_protocols': { value: null, type: 'yaml' }, + location_name: { value: 'A private location', type: 'text' }, + id: { value: '77f25200-7cf3-450d-abfa-4f95faae1907', type: 'text' }, + config_id: { value: '77f25200-7cf3-450d-abfa-4f95faae1907', type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { value: 'ui', type: 'text' }, + 'monitor.project.id': { value: null, type: 'text' }, + 'monitor.project.name': { value: null, type: 'text' }, + }, + id: 'synthetics/tcp-tcp-77f25200-7cf3-450d-abfa-4f95faae1907-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + __ui: { is_tls_enabled: false, is_zip_url_tls_enabled: false }, + type: 'tcp', + name: 'Invalid tcp monitor with 8 minute schedule', + id: '77f25200-7cf3-450d-abfa-4f95faae1907', + origin: 'ui', + 'run_from.id': 'A private location', + 'run_from.geo.name': 'A private location', + enabled: true, + hosts: 'localhost:5601', + schedule: '@every 8m', + timeout: '16s', + proxy_use_local_resolver: false, + processors: [ + { + add_fields: { + target: '', + fields: { + 'monitor.fleet_managed': true, + config_id: '77f25200-7cf3-450d-abfa-4f95faae1907', + }, + }, + }, + ], + }, + }, + ], + }, + { + type: 'synthetics/icmp', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'icmp' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'icmp', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + wait: { value: '1s', type: 'text' }, + hosts: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + tags: { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/icmp-icmp-77f25200-7cf3-450d-abfa-4f95faae1907-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + }, + ], + }, + { + type: 'synthetics/browser', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'browser', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + tags: { type: 'yaml' }, + 'source.zip_url.url': { type: 'text' }, + 'source.zip_url.username': { type: 'text' }, + 'source.zip_url.folder': { type: 'text' }, + 'source.zip_url.password': { type: 'password' }, + 'source.inline.script': { type: 'yaml' }, + 'source.project.content': { type: 'text' }, + params: { type: 'yaml' }, + playwright_options: { type: 'yaml' }, + screenshots: { type: 'text' }, + synthetics_args: { type: 'text' }, + ignore_https_errors: { type: 'bool' }, + 'throttling.config': { type: 'text' }, + 'filter_journeys.tags': { type: 'yaml' }, + 'filter_journeys.match': { type: 'text' }, + 'source.zip_url.ssl.certificate_authorities': { type: 'yaml' }, + 'source.zip_url.ssl.certificate': { type: 'yaml' }, + 'source.zip_url.ssl.key': { type: 'yaml' }, + 'source.zip_url.ssl.key_passphrase': { type: 'text' }, + 'source.zip_url.ssl.verification_mode': { type: 'text' }, + 'source.zip_url.ssl.supported_protocols': { type: 'yaml' }, + 'source.zip_url.proxy_url': { type: 'text' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/browser-browser-77f25200-7cf3-450d-abfa-4f95faae1907-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + __ui: null, + type: 'browser', + name: null, + 'run_from.id': 'Fleet managed', + 'run_from.geo.name': 'Fleet managed', + enabled: true, + schedule: '@every 3m', + timeout: null, + throttling: null, + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.network', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + id: 'synthetics/browser-browser.network-77f25200-7cf3-450d-abfa-4f95faae1907-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.screenshot', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + id: 'synthetics/browser-browser.screenshot-77f25200-7cf3-450d-abfa-4f95faae1907-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + ], + }, + ], + is_managed: true, + revision: 1, + created_at: '2023-04-19T15:35:26.839Z', + created_by: 'system', + updated_at: '2023-04-19T15:35:26.839Z', + updated_by: 'system', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-04-19T15:35:26.884Z', + created_at: '2023-04-19T15:35:26.884Z', + typeMigrationVersion: '8.7.0', +} as unknown as SavedObjectUnsanitizedDoc; +export const icmpPolicy = { + type: 'ingest-package-policies', + id: 'e437c11c-5cb0-42d1-966f-d2ba231550fd-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + attributes: { + name: 'Invalid ICMP monitor with 11 minute schedule-A private location-default', + namespace: 'default', + package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.11.8' }, + enabled: true, + policy_id: 'fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1', + inputs: [ + { + type: 'synthetics/http', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'http' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'http', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + urls: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + max_redirects: { type: 'integer' }, + proxy_url: { type: 'text' }, + tags: { type: 'yaml' }, + username: { type: 'text' }, + password: { type: 'password' }, + 'response.include_headers': { type: 'bool' }, + 'response.include_body': { type: 'text' }, + 'check.request.method': { type: 'text' }, + 'check.request.headers': { type: 'yaml' }, + 'check.request.body': { type: 'yaml' }, + 'check.response.status': { type: 'yaml' }, + 'check.response.headers': { type: 'yaml' }, + 'check.response.body.positive': { type: 'yaml' }, + 'check.response.body.negative': { type: 'yaml' }, + 'ssl.certificate_authorities': { type: 'yaml' }, + 'ssl.certificate': { type: 'yaml' }, + 'ssl.key': { type: 'yaml' }, + 'ssl.key_passphrase': { type: 'text' }, + 'ssl.verification_mode': { type: 'text' }, + 'ssl.supported_protocols': { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/http-http-e437c11c-5cb0-42d1-966f-d2ba231550fd-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + }, + ], + }, + { + type: 'synthetics/tcp', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'tcp' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'tcp', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + hosts: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + proxy_url: { type: 'text' }, + proxy_use_local_resolver: { value: false, type: 'bool' }, + tags: { type: 'yaml' }, + 'check.send': { type: 'text' }, + 'check.receive': { type: 'text' }, + 'ssl.certificate_authorities': { type: 'yaml' }, + 'ssl.certificate': { type: 'yaml' }, + 'ssl.key': { type: 'yaml' }, + 'ssl.key_passphrase': { type: 'text' }, + 'ssl.verification_mode': { type: 'text' }, + 'ssl.supported_protocols': { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/tcp-tcp-e437c11c-5cb0-42d1-966f-d2ba231550fd-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + }, + ], + }, + { + type: 'synthetics/icmp', + policy_template: 'synthetics', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'icmp', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'icmp', type: 'text' }, + name: { value: 'Invalid ICMP monitor with 11 minute schedule', type: 'text' }, + schedule: { value: '"@every 16m"', type: 'text' }, + wait: { value: '1s', type: 'text' }, + hosts: { value: '1.1.1.1', type: 'text' }, + 'service.name': { value: '', type: 'text' }, + timeout: { value: '16s', type: 'text' }, + tags: { value: null, type: 'yaml' }, + location_name: { value: 'A private location', type: 'text' }, + id: { value: 'e437c11c-5cb0-42d1-966f-d2ba231550fd', type: 'text' }, + config_id: { value: 'e437c11c-5cb0-42d1-966f-d2ba231550fd', type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { value: 'ui', type: 'text' }, + 'monitor.project.id': { value: null, type: 'text' }, + 'monitor.project.name': { value: null, type: 'text' }, + }, + id: 'synthetics/icmp-icmp-e437c11c-5cb0-42d1-966f-d2ba231550fd-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + __ui: null, + type: 'icmp', + name: 'Invalid ICMP monitor with 11 minute schedule', + id: 'e437c11c-5cb0-42d1-966f-d2ba231550fd', + origin: 'ui', + 'run_from.id': 'A private location', + 'run_from.geo.name': 'A private location', + enabled: true, + hosts: '1.1.1.1', + schedule: '@every 16m', + wait: '1s', + timeout: '16s', + processors: [ + { + add_fields: { + target: '', + fields: { + 'monitor.fleet_managed': true, + config_id: 'e437c11c-5cb0-42d1-966f-d2ba231550fd', + }, + }, + }, + ], + }, + }, + ], + }, + { + type: 'synthetics/browser', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'browser', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + tags: { type: 'yaml' }, + 'source.zip_url.url': { type: 'text' }, + 'source.zip_url.username': { type: 'text' }, + 'source.zip_url.folder': { type: 'text' }, + 'source.zip_url.password': { type: 'password' }, + 'source.inline.script': { type: 'yaml' }, + 'source.project.content': { type: 'text' }, + params: { type: 'yaml' }, + playwright_options: { type: 'yaml' }, + screenshots: { type: 'text' }, + synthetics_args: { type: 'text' }, + ignore_https_errors: { type: 'bool' }, + 'throttling.config': { type: 'text' }, + 'filter_journeys.tags': { type: 'yaml' }, + 'filter_journeys.match': { type: 'text' }, + 'source.zip_url.ssl.certificate_authorities': { type: 'yaml' }, + 'source.zip_url.ssl.certificate': { type: 'yaml' }, + 'source.zip_url.ssl.key': { type: 'yaml' }, + 'source.zip_url.ssl.key_passphrase': { type: 'text' }, + 'source.zip_url.ssl.verification_mode': { type: 'text' }, + 'source.zip_url.ssl.supported_protocols': { type: 'yaml' }, + 'source.zip_url.proxy_url': { type: 'text' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/browser-browser-e437c11c-5cb0-42d1-966f-d2ba231550fd-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + __ui: null, + type: 'browser', + name: null, + 'run_from.id': 'Fleet managed', + 'run_from.geo.name': 'Fleet managed', + enabled: true, + schedule: '@every 3m', + timeout: null, + throttling: null, + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.network', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + id: 'synthetics/browser-browser.network-e437c11c-5cb0-42d1-966f-d2ba231550fd-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.screenshot', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + id: 'synthetics/browser-browser.screenshot-e437c11c-5cb0-42d1-966f-d2ba231550fd-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + ], + }, + ], + is_managed: true, + revision: 1, + created_at: '2023-04-19T15:35:53.763Z', + created_by: 'system', + updated_at: '2023-04-19T15:35:53.763Z', + updated_by: 'system', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-04-19T15:35:53.793Z', + created_at: '2023-04-19T15:35:53.793Z', + typeMigrationVersion: '8.7.0', +} as unknown as SavedObjectUnsanitizedDoc; + +export const getBrowserPolicy = (throttling = '5d/3u/20l') => + ({ + type: 'ingest-package-policies', + id: '420754e9-40f2-486c-bc2e-265bafd735c5-fe200580-dee2-11ed-933e-0f85f8c5dd40-default', + attributes: { + name: 'https://elastic.co-A private location-default', + namespace: 'default', + package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.11.8' }, + enabled: true, + policy_id: 'fe200580-dee2-11ed-933e-0f85f8c5dd40', + inputs: [ + { + type: 'synthetics/http', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'http' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'http', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + urls: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + max_redirects: { type: 'integer' }, + proxy_url: { type: 'text' }, + tags: { type: 'yaml' }, + username: { type: 'text' }, + password: { type: 'password' }, + 'response.include_headers': { type: 'bool' }, + 'response.include_body': { type: 'text' }, + 'check.request.method': { type: 'text' }, + 'check.request.headers': { type: 'yaml' }, + 'check.request.body': { type: 'yaml' }, + 'check.response.status': { type: 'yaml' }, + 'check.response.headers': { type: 'yaml' }, + 'check.response.body.positive': { type: 'yaml' }, + 'check.response.body.negative': { type: 'yaml' }, + 'ssl.certificate_authorities': { type: 'yaml' }, + 'ssl.certificate': { type: 'yaml' }, + 'ssl.key': { type: 'yaml' }, + 'ssl.key_passphrase': { type: 'text' }, + 'ssl.verification_mode': { type: 'text' }, + 'ssl.supported_protocols': { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/http-http-420754e9-40f2-486c-bc2e-265bafd735c5-fe200580-dee2-11ed-933e-0f85f8c5dd40-default', + }, + ], + }, + { + type: 'synthetics/tcp', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'tcp' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'tcp', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + hosts: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + proxy_url: { type: 'text' }, + proxy_use_local_resolver: { value: false, type: 'bool' }, + tags: { type: 'yaml' }, + 'check.send': { type: 'text' }, + 'check.receive': { type: 'text' }, + 'ssl.certificate_authorities': { type: 'yaml' }, + 'ssl.certificate': { type: 'yaml' }, + 'ssl.key': { type: 'yaml' }, + 'ssl.key_passphrase': { type: 'text' }, + 'ssl.verification_mode': { type: 'text' }, + 'ssl.supported_protocols': { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/tcp-tcp-420754e9-40f2-486c-bc2e-265bafd735c5-fe200580-dee2-11ed-933e-0f85f8c5dd40-default', + }, + ], + }, + { + type: 'synthetics/icmp', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'icmp' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'icmp', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + wait: { value: '1s', type: 'text' }, + hosts: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + tags: { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/icmp-icmp-420754e9-40f2-486c-bc2e-265bafd735c5-fe200580-dee2-11ed-933e-0f85f8c5dd40-default', + }, + ], + }, + { + type: 'synthetics/browser', + policy_template: 'synthetics', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser', + elasticsearch: { + privileges: { indices: ['auto_configure', 'create_doc', 'read'] }, + }, + }, + vars: { + __ui: { + value: + '{"script_source":{"is_generated_script":false,"file_name":""},"is_zip_url_tls_enabled":false,"is_tls_enabled":false}', + type: 'yaml', + }, + enabled: { value: true, type: 'bool' }, + type: { value: 'browser', type: 'text' }, + name: { value: 'https://elastic.co', type: 'text' }, + schedule: { value: '"@every 2m"', type: 'text' }, + 'service.name': { value: '', type: 'text' }, + timeout: { value: null, type: 'text' }, + tags: { value: null, type: 'yaml' }, + 'source.zip_url.url': { value: '', type: 'text' }, + 'source.zip_url.username': { value: '', type: 'text' }, + 'source.zip_url.folder': { value: '', type: 'text' }, + 'source.zip_url.password': { value: '', type: 'password' }, + 'source.inline.script': { + value: + "\"step('Go to https://elastic.co', async () => {\\n await page.goto('https://elastic.co');\\n});\"", + type: 'yaml', + }, + 'source.project.content': { value: '', type: 'text' }, + params: { value: '', type: 'yaml' }, + playwright_options: { value: '', type: 'yaml' }, + screenshots: { value: 'on', type: 'text' }, + synthetics_args: { value: null, type: 'text' }, + ignore_https_errors: { value: false, type: 'bool' }, + 'throttling.config': { value: throttling, type: 'text' }, + 'filter_journeys.tags': { value: null, type: 'yaml' }, + 'filter_journeys.match': { value: null, type: 'text' }, + 'source.zip_url.ssl.certificate_authorities': { value: null, type: 'yaml' }, + 'source.zip_url.ssl.certificate': { value: null, type: 'yaml' }, + 'source.zip_url.ssl.key': { value: null, type: 'yaml' }, + 'source.zip_url.ssl.key_passphrase': { value: null, type: 'text' }, + 'source.zip_url.ssl.verification_mode': { value: null, type: 'text' }, + 'source.zip_url.ssl.supported_protocols': { value: null, type: 'yaml' }, + 'source.zip_url.proxy_url': { value: '', type: 'text' }, + location_name: { value: 'A private location', type: 'text' }, + id: { value: '420754e9-40f2-486c-bc2e-265bafd735c5', type: 'text' }, + config_id: { value: '420754e9-40f2-486c-bc2e-265bafd735c5', type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { value: 'ui', type: 'text' }, + 'monitor.project.id': { value: null, type: 'text' }, + 'monitor.project.name': { value: null, type: 'text' }, + }, + id: 'synthetics/browser-browser-420754e9-40f2-486c-bc2e-265bafd735c5-fe200580-dee2-11ed-933e-0f85f8c5dd40-default', + compiled_stream: { + __ui: { + script_source: { is_generated_script: false, file_name: '' }, + is_zip_url_tls_enabled: false, + is_tls_enabled: false, + }, + type: 'browser', + name: 'https://elastic.co', + id: '420754e9-40f2-486c-bc2e-265bafd735c5', + origin: 'ui', + 'run_from.id': 'A private location', + 'run_from.geo.name': 'A private location', + enabled: true, + schedule: '@every 2m', + timeout: null, + throttling: throttling === 'false' ? false : throttling, + 'source.inline.script': + "step('Go to https://elastic.co', async () => {\n await page.goto('https://elastic.co');\n});", + screenshots: 'on', + processors: [ + { + add_fields: { + target: '', + fields: { + 'monitor.fleet_managed': true, + config_id: '420754e9-40f2-486c-bc2e-265bafd735c5', + }, + }, + }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.network', + elasticsearch: { + privileges: { indices: ['auto_configure', 'create_doc', 'read'] }, + }, + }, + id: 'synthetics/browser-browser.network-420754e9-40f2-486c-bc2e-265bafd735c5-fe200580-dee2-11ed-933e-0f85f8c5dd40-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.screenshot', + elasticsearch: { + privileges: { indices: ['auto_configure', 'create_doc', 'read'] }, + }, + }, + id: 'synthetics/browser-browser.screenshot-420754e9-40f2-486c-bc2e-265bafd735c5-fe200580-dee2-11ed-933e-0f85f8c5dd40-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + ], + }, + ], + is_managed: true, + revision: 1, + created_at: '2023-04-19T18:55:35.126Z', + created_by: 'system', + updated_at: '2023-04-19T18:55:35.126Z', + updated_by: 'system', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-04-19T18:55:35.250Z', + created_at: '2023-04-19T18:55:35.250Z', + typeMigrationVersion: '8.7.0', + } as unknown as SavedObjectUnsanitizedDoc); diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/index.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/index.ts new file mode 100644 index 00000000000000..b38d03e10a104f --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/index.ts @@ -0,0 +1,8 @@ +/* + * 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 { migratePackagePolicyToV880 } from './to_v8_8_0'; diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/to_v8_8_0.test.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/to_v8_8_0.test.ts new file mode 100644 index 00000000000000..23d7462872c398 --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/to_v8_8_0.test.ts @@ -0,0 +1,167 @@ +/* + * 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 { SavedObjectMigrationContext } from '@kbn/core/server'; + +import { getBrowserPolicy, httpPolicy, icmpPolicy, tcpPolicy } from './fixtures/8.7.0'; + +import { migratePackagePolicyToV880 as migration } from './to_v8_8_0'; + +describe('8.8.0 Synthetics Package Policy migration', () => { + describe('schedule migration', () => { + const testSchedules = [ + ['4', '3'], + ['4.5', '5'], + ['7', '5'], + ['8', '10'], + ['9.5', '10'], + ['12', '10'], + ['13', '15'], + ['16', '15'], + ['18', '20'], + ['21', '20'], + ['25', '20'], + ['26', '30'], + ['31', '30'], + ['45', '30'], + ['46', '60'], + ['61', '60'], + ['90', '60'], + ['91', '120'], + ['121', '120'], + ['195', '240'], + ['600', '240'], + ]; + + it.each(testSchedules)('handles a variety of schedules', (invalidSchedule, validSchedule) => { + const actual = migration( + { + ...httpPolicy, + attributes: { + ...httpPolicy.attributes, + inputs: [ + { + ...httpPolicy.attributes.inputs[0], + streams: [ + { + ...httpPolicy.attributes.inputs[0].streams[0], + vars: { + ...httpPolicy.attributes.inputs[0].streams[0].vars, + schedule: { + value: `"@every ${invalidSchedule}m"`, + type: 'text', + }, + }, + }, + ], + }, + ], + }, + }, + {} as SavedObjectMigrationContext + ); + expect(actual.attributes?.inputs[0]?.streams[0]?.vars?.schedule?.value).toEqual( + `"@every ${validSchedule}m"` + ); + expect(actual.attributes?.inputs[0]?.streams[0]?.compiled_stream?.schedule).toEqual( + `@every ${validSchedule}m` + ); + }); + + it('handles browserPolicy with 2 minute', () => { + const actual = migration(getBrowserPolicy(), {} as SavedObjectMigrationContext); + expect(actual.attributes?.inputs[3]?.streams[0]?.vars?.schedule?.value).toEqual( + '"@every 1m"' + ); + expect(actual.attributes?.inputs[3]?.streams[0]?.compiled_stream?.schedule).toEqual( + `@every 1m` + ); + }); + + it('handles httpPolicy with 4 minute schedule', () => { + const actual = migration(httpPolicy, {} as SavedObjectMigrationContext); + expect(actual.attributes?.inputs[0]?.streams[0]?.vars?.schedule?.value).toEqual( + '"@every 3m"' + ); + expect(actual.attributes?.inputs[0]?.streams[0]?.compiled_stream?.schedule).toEqual( + `@every 3m` + ); + }); + + it('handles tcp with 8 minute schedule', () => { + const actual = migration(tcpPolicy, {} as SavedObjectMigrationContext); + expect(actual.attributes?.inputs[1]?.streams[0]?.vars?.schedule?.value).toEqual( + '"@every 10m"' + ); + expect(actual.attributes?.inputs[1]?.streams[0]?.compiled_stream?.schedule).toEqual( + `@every 10m` + ); + }); + + it('handles icmpPolicy with 16 minute schedule', () => { + const actual = migration(icmpPolicy, {} as SavedObjectMigrationContext); + expect(actual.attributes?.inputs[2]?.streams[0]?.vars?.schedule?.value).toEqual( + '"@every 15m"' + ); + expect(actual.attributes?.inputs[2]?.streams[0]?.compiled_stream?.schedule).toEqual( + `@every 15m` + ); + }); + }); + + describe('throttling migration', () => { + it('handles throtling config for throttling: false', () => { + const actual = migration(getBrowserPolicy('false'), {} as SavedObjectMigrationContext); + expect(actual.attributes?.inputs[3]?.streams[0]?.vars?.['throttling.config']?.value).toEqual( + 'false' + ); + expect(actual.attributes?.inputs[3]?.streams[0]?.compiled_stream?.throttling).toEqual(false); + }); + + it('handles throttling config for default throttling', () => { + const actual = migration(getBrowserPolicy(), {} as SavedObjectMigrationContext); + expect(actual.attributes?.inputs[3]?.streams[0]?.vars?.['throttling.config']?.value).toEqual( + JSON.stringify({ download: 5, upload: 3, latency: 20 }) + ); + expect(actual.attributes?.inputs[3]?.streams[0]?.compiled_stream.throttling).toEqual({ + download: 5, + upload: 3, + latency: 20, + }); + }); + + it('handles throttling config for custom throttling', () => { + const actual = migration( + getBrowserPolicy('1.6d/0.75u/150l'), + {} as SavedObjectMigrationContext + ); + expect(actual.attributes?.inputs[3]?.streams[0]?.vars?.['throttling.config']?.value).toEqual( + JSON.stringify({ download: 1.6, upload: 0.75, latency: 150 }) + ); + expect(actual.attributes?.inputs[3]?.streams[0]?.compiled_stream.throttling).toEqual({ + download: 1.6, + upload: 0.75, + latency: 150, + }); + }); + + it('handles edge cases', () => { + const actual = migration( + getBrowserPolicy('not a valid value'), + {} as SavedObjectMigrationContext + ); + expect(actual.attributes?.inputs[3]?.streams[0]?.vars?.['throttling.config']?.value).toEqual( + JSON.stringify({ download: 5, upload: 3, latency: 20 }) + ); + expect(actual.attributes?.inputs[3]?.streams[0]?.compiled_stream.throttling).toEqual({ + download: 5, + upload: 3, + latency: 20, + }); + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/to_v8_8_0.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/to_v8_8_0.ts new file mode 100644 index 00000000000000..872313471ae9bc --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/to_v8_8_0.ts @@ -0,0 +1,107 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SavedObjectMigrationFn, SavedObjectUnsanitizedDoc } from '@kbn/core/server'; + +import type { PackagePolicy } from '../../../../common'; + +export const ALLOWED_SCHEDULES_IN_MINUTES = [ + '1', + '3', + '5', + '10', + '15', + '20', + '30', + '60', + '120', + '240', +]; + +export const migratePackagePolicyToV880: SavedObjectMigrationFn = ( + packagePolicyDoc +) => { + if (packagePolicyDoc.attributes.package?.name !== 'synthetics') { + return packagePolicyDoc; + } + + const updatedPackagePolicyDoc: SavedObjectUnsanitizedDoc = packagePolicyDoc; + + const enabledInput = updatedPackagePolicyDoc.attributes.inputs.find( + (input) => input.enabled === true + ); + const enabledStream = enabledInput?.streams.find((stream) => { + return ['browser', 'http', 'icmp', 'tcp'].includes(stream.data_stream.dataset); + }); + if (!enabledStream) { + return updatedPackagePolicyDoc; + } + + if ( + enabledStream.vars && + enabledStream.vars.schedule?.value && + enabledStream.compiled_stream?.schedule + ) { + const schedule = enabledStream.vars.schedule.value.match(/\d+\.?\d*/g)?.[0]; + const updatedSchedule = getNearestSupportedSchedule(schedule); + const formattedUpdatedSchedule = `@every ${updatedSchedule}m`; + enabledStream.vars.schedule.value = `"${formattedUpdatedSchedule}"`; + enabledStream.compiled_stream.schedule = formattedUpdatedSchedule; + } + + if ( + enabledStream.data_stream.dataset === 'browser' && + enabledStream.vars?.['throttling.config'] && + enabledStream.compiled_stream?.throttling + ) { + const throttling = enabledStream.vars['throttling.config'].value; + if (throttling) { + const formattedThrottling = handleThrottling(throttling); + enabledStream.vars['throttling.config'].value = JSON.stringify(formattedThrottling); + enabledStream.compiled_stream.throttling = formattedThrottling; + } + } + + return updatedPackagePolicyDoc; +}; + +const handleThrottling = ( + throttling: string +): { download: number; upload: number; latency: number } => { + try { + const [download = 5, upload = 3, latency = 20] = throttling.match(/\d+\.?\d*/g) || []; + return { + download: Number(download), + upload: Number(upload), + latency: Number(latency), + }; + } catch { + return { + download: 5, + upload: 3, + latency: 20, + }; + } +}; + +const getNearestSupportedSchedule = (currentSchedule: string): string => { + try { + const closest = ALLOWED_SCHEDULES_IN_MINUTES.reduce(function (prev, curr) { + const supportedSchedule = parseFloat(curr); + const currSchedule = parseFloat(currentSchedule); + const prevSupportedSchedule = parseFloat(prev); + return Math.abs(supportedSchedule - currSchedule) < + Math.abs(prevSupportedSchedule - currSchedule) + ? curr + : prev; + }); + + return closest; + } catch { + return '10'; + } +}; diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_8_0.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_8_0.ts new file mode 100644 index 00000000000000..ab68e4e1c1429e --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_8_0.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SavedObjectMigrationFn } from '@kbn/core/server'; + +import type { PackagePolicy } from '../../../common'; + +import { migratePackagePolicyToV880 as SecSolMigratePackagePolicyToV880 } from './security_solution'; +import { migratePackagePolicyToV880 as SyntheticsMigratePackagePolicyToV880 } from './synthetics'; + +export const migratePackagePolicyToV880: SavedObjectMigrationFn = ( + packagePolicyDoc, + migrationContext +) => { + let updatedPackagePolicyDoc = packagePolicyDoc; + + // Endpoint specific migrations + if (packagePolicyDoc.attributes.package?.name === 'endpoint') { + updatedPackagePolicyDoc = SecSolMigratePackagePolicyToV880(packagePolicyDoc, migrationContext); + } + + // Synthetics specific migrations + if (packagePolicyDoc.attributes.package?.name === 'synthetics') { + updatedPackagePolicyDoc = SyntheticsMigratePackagePolicyToV880( + packagePolicyDoc, + migrationContext + ); + } + + return updatedPackagePolicyDoc; +}; diff --git a/x-pack/plugins/fleet/server/types/models/package_policy.ts b/x-pack/plugins/fleet/server/types/models/package_policy.ts index b94becf1fe449c..8e60621102164f 100644 --- a/x-pack/plugins/fleet/server/types/models/package_policy.ts +++ b/x-pack/plugins/fleet/server/types/models/package_policy.ts @@ -99,6 +99,7 @@ const PackagePolicyBaseSchema = { namespace: NamespaceSchema, policy_id: schema.string(), enabled: schema.boolean(), + is_managed: schema.maybe(schema.boolean()), package: schema.maybe( schema.object({ name: schema.string(), diff --git a/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts b/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts index 6b9cc7913019a8..adfa002e8e20a6 100644 --- a/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts +++ b/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts @@ -48,9 +48,11 @@ export const CUSTOM_LABEL = i18n.translate('xpack.synthetics.connectionProfile.c defaultMessage: 'Custom', }); +export const DEFAULT_THROTTLING_VALUE = { download: '5', upload: '3', latency: '20' }; + export const PROFILE_VALUES: ThrottlingConfig[] = [ { - value: { download: '5', upload: '3', latency: '20' }, + value: DEFAULT_THROTTLING_VALUE, id: PROFILE_VALUES_ENUM.DEFAULT, label: i18n.translate('xpack.synthetics.connectionProfile.default', { defaultMessage: 'Default', diff --git a/x-pack/plugins/synthetics/common/formatters/browser/formatters.test.ts b/x-pack/plugins/synthetics/common/formatters/browser/formatters.test.ts new file mode 100644 index 00000000000000..7883e0aa09bcac --- /dev/null +++ b/x-pack/plugins/synthetics/common/formatters/browser/formatters.test.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ConfigKey } from '../../runtime_types'; +import { throttlingFormatter } from './formatters'; + +describe('formatters', () => { + describe('throttling formatter', () => { + it('formats for no throttling', () => { + expect( + throttlingFormatter!( + { + [ConfigKey.THROTTLING_CONFIG]: { + value: { + download: '0', + upload: '0', + latency: '0', + }, + label: 'No throttling', + id: 'no-throttling', + }, + }, + ConfigKey.THROTTLING_CONFIG + ) + ).toEqual('false'); + }); + + it('formats for default throttling', () => { + expect( + throttlingFormatter!( + { + [ConfigKey.THROTTLING_CONFIG]: { + value: { + download: '5', + upload: '3', + latency: '20', + }, + label: 'Default', + id: 'default', + }, + }, + ConfigKey.THROTTLING_CONFIG + ) + ).toEqual(JSON.stringify({ download: 5, upload: 3, latency: 20 })); + }); + + it('formats for custom throttling', () => { + expect( + throttlingFormatter!( + { + [ConfigKey.THROTTLING_CONFIG]: { + value: { + download: '1.25', + upload: '0.75', + latency: '150', + }, + label: 'Custom', + id: 'custom', + }, + }, + ConfigKey.THROTTLING_CONFIG + ) + ).toEqual(JSON.stringify({ download: 1.25, upload: 0.75, latency: 150 })); + }); + }); +}); diff --git a/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts b/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts index 508464c48a5f29..9dfa027767851f 100644 --- a/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts +++ b/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts @@ -12,6 +12,7 @@ import { objectToJsonFormatter, stringToJsonFormatter, } from '../formatting_utils'; +import { DEFAULT_THROTTLING_VALUE } from '../../constants/monitor_defaults'; import { tlsFormatters } from '../tls/formatters'; @@ -24,7 +25,11 @@ export const throttlingFormatter: Formatter = (fields) => { return 'false'; } - return `${throttling.value.download}d/${throttling.value.upload}u/${throttling.value.latency}l`; + return JSON.stringify({ + download: Number(throttling?.value?.download || DEFAULT_THROTTLING_VALUE.download), + upload: Number(throttling?.value?.upload || DEFAULT_THROTTLING_VALUE.upload), + latency: Number(throttling?.value?.latency || DEFAULT_THROTTLING_VALUE), + }); }; export const browserFormatters: BrowserFormatMap = { diff --git a/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.test.ts b/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.test.ts index 6be823ee0a2080..2706972456acdf 100644 --- a/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.test.ts +++ b/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.test.ts @@ -422,7 +422,7 @@ describe('formatSyntheticsPolicy', () => { }, 'throttling.config': { type: 'text', - value: '5d/3u/20l', + value: JSON.stringify({ download: 5, upload: 3, latency: 20 }), }, timeout: { type: 'text', diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/services/add_monitor.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/services/add_monitor.ts index 44053aa29ed26d..fb79e0ec94ff2a 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/services/add_monitor.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/services/add_monitor.ts @@ -130,11 +130,15 @@ export const testDataMonitor = { 'filter_journeys.match': '', 'filter_journeys.tags': [], ignore_https_errors: false, - 'throttling.is_enabled': true, - 'throttling.download_speed': '5', - 'throttling.upload_speed': '3', - 'throttling.latency': '20', - 'throttling.config': '5d/3u/20l', + throttling: { + id: 'custom', + label: 'Custom', + value: { + download: '5', + upload: '3', + latency: '20', + }, + }, 'ssl.certificate_authorities': '', 'ssl.certificate': '', 'ssl.key': '', diff --git a/x-pack/plugins/synthetics/e2e/tasks/import_monitors.ts b/x-pack/plugins/synthetics/e2e/tasks/import_monitors.ts index f65d82a9933f43..f7143ee5b89e80 100644 --- a/x-pack/plugins/synthetics/e2e/tasks/import_monitors.ts +++ b/x-pack/plugins/synthetics/e2e/tasks/import_monitors.ts @@ -48,11 +48,15 @@ export const importMonitors = async ({ 'filter_journeys.match': '', 'filter_journeys.tags': [], ignore_https_errors: false, - 'throttling.is_enabled': true, - 'throttling.download_speed': '5', - 'throttling.upload_speed': '3', - 'throttling.latency': '20', - 'throttling.config': '5d/3u/20l', + throttling: { + id: 'custom', + label: 'Custom', + value: { + download: '5', + upload: '3', + latency: '20', + }, + }, }; const id = '1c215bd0-f580-11ec-89e5-694db461b7a5'; diff --git a/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.test.ts b/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.test.ts index 367c27c4bca082..28d7018448374b 100644 --- a/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.test.ts +++ b/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.test.ts @@ -169,11 +169,15 @@ const testMonitors = [ 'filter_journeys.match': '', 'filter_journeys.tags': [], ignore_https_errors: false, - 'throttling.is_enabled': true, - 'throttling.download_speed': '5', - 'throttling.upload_speed': '3', - 'throttling.latency': '20', - 'throttling.config': '5d/3u/20l', + throttling: { + id: 'custom', + label: 'Custom', + value: { + download: '5', + upload: '3', + latency: '20', + }, + }, 'ssl.certificate_authorities': '', 'ssl.certificate': '', 'ssl.verification_mode': 'full', diff --git a/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.test.ts index a334b6b406b3be..f1ebab30182cea 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.test.ts @@ -263,7 +263,7 @@ describe('SyntheticsPrivateLocation', () => { }, 'throttling.config': { type: 'text', - value: '5d/3u/20l', + value: JSON.stringify({ download: 5, upload: 3, latency: 20 }), }, timeout: { type: 'text', diff --git a/x-pack/test/api_integration/apis/synthetics/sample_data/test_browser_policy.ts b/x-pack/test/api_integration/apis/synthetics/sample_data/test_browser_policy.ts index 0cebf231cf7872..eff1026cb4486b 100644 --- a/x-pack/test/api_integration/apis/synthetics/sample_data/test_browser_policy.ts +++ b/x-pack/test/api_integration/apis/synthetics/sample_data/test_browser_policy.ts @@ -185,7 +185,10 @@ export const getTestBrowserSyntheticsPolicy = ({ screenshots: { value: 'on', type: 'text' }, synthetics_args: { value: null, type: 'text' }, ignore_https_errors: { value: false, type: 'bool' }, - 'throttling.config': { value: '5d/3u/20l', type: 'text' }, + 'throttling.config': { + value: JSON.stringify({ download: 5, upload: 3, latency: 20 }), + type: 'text', + }, 'filter_journeys.tags': { value: null, type: 'yaml' }, 'filter_journeys.match': { value: null, type: 'text' }, 'source.zip_url.ssl.certificate_authorities': { type: 'yaml' }, @@ -218,7 +221,7 @@ export const getTestBrowserSyntheticsPolicy = ({ enabled: true, schedule: '@every 3m', timeout: '16s', - throttling: '5d/3u/20l', + throttling: { download: 5, upload: 3, latency: 20 }, tags: ['cookie-test', 'browser'], 'source.inline.script': 'step("Visit /users api route", async () => {\\n const response = await page.goto(\'https://nextjs-test-synthetics.vercel.app/api/users\');\\n expect(response.status()).toEqual(200);\\n});', diff --git a/x-pack/test/api_integration/apis/synthetics/sample_data/test_project_monitor_policy.ts b/x-pack/test/api_integration/apis/synthetics/sample_data/test_project_monitor_policy.ts index cd5ea22451b927..ba48a26ff50fe7 100644 --- a/x-pack/test/api_integration/apis/synthetics/sample_data/test_project_monitor_policy.ts +++ b/x-pack/test/api_integration/apis/synthetics/sample_data/test_project_monitor_policy.ts @@ -213,7 +213,10 @@ export const getTestProjectSyntheticsPolicy = ( screenshots: { value: 'on', type: 'text' }, synthetics_args: { value: null, type: 'text' }, ignore_https_errors: { value: false, type: 'bool' }, - 'throttling.config': { value: '5d/3u/20l', type: 'text' }, + 'throttling.config': { + value: JSON.stringify({ download: 5, upload: 3, latency: 20 }), + type: 'text', + }, 'filter_journeys.tags': { value: null, type: 'yaml' }, 'filter_journeys.match': { value: '"check if title is present"', type: 'text' }, 'source.zip_url.ssl.certificate_authorities': { type: 'yaml' }, @@ -246,7 +249,7 @@ export const getTestProjectSyntheticsPolicy = ( 'run_from.geo.name': locationName, 'run_from.id': locationName, timeout: null, - throttling: '5d/3u/20l', + throttling: { download: 5, upload: 3, latency: 20 }, 'source.project.content': 'UEsDBBQACAAIAON5qVQAAAAAAAAAAAAAAAAfAAAAZXhhbXBsZXMvdG9kb3MvYmFzaWMuam91cm5leS50c22Q0WrDMAxF3/sVF7MHB0LMXlc6RvcN+wDPVWNviW0sdUsp/fe5SSiD7UFCWFfHujIGlpnkybwxFTZfoY/E3hsaLEtwhs9RPNWKDU12zAOxkXRIbN4tB9d9pFOJdO6EN2HMqQguWN9asFBuQVMmJ7jiWNII9fIXrbabdUYr58l9IhwhQQZCYORCTFFUC31Btj21NRc7Mq4Nds+4bDD/pNVgT9F52Jyr2Fa+g75LAPttg8yErk+S9ELpTmVotlVwnfNCuh2lepl3+JflUmSBJ3uggt1v9INW/lHNLKze9dJe1J3QJK8pSvWkm6aTtCet5puq+x63+AFQSwcIAPQ3VfcAAACcAQAAUEsBAi0DFAAIAAgA43mpVAD0N1X3AAAAnAEAAB8AAAAAAAAAAAAgAKSBAAAAAGV4YW1wbGVzL3RvZG9zL2Jhc2ljLmpvdXJuZXkudHNQSwUGAAAAAAEAAQBNAAAARAEAAAAA', playwright_options: { headless: true, chromiumSandbox: false },