From 18963b1954b2cc7127e20a7c848f2a1be216bd2c Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Wed, 9 Jun 2021 15:21:23 -0700 Subject: [PATCH] [Fleet] Support granular integrations in policy editor (#101531) * Adjust packageToPackagePolicy to support multiple policy_templates * Adjust validatePackagePolicy to support multiple policy templates * Create input for every policy template input instead of grouping * Related adjustments for previous commit * Don't key by policy template when package doesn't have integrations * Adjust limited package check to support multiple policy templates * First pass at UI support * Fix imports, add missing schema * Fix finding input agent templates, adjust tests * Only enable specific integrations by default in the UI * Fix import * Make breadcrumbs and create package policy page title reflect integration, not package * Only show specific integration inputs when adding integration to a policy * Change predicate * Simplify condition --- .../package_to_package_policy.test.ts.snap | 804 +++++ .../validate_package_policy.test.ts.snap | 301 ++ .../common/services/fixtures/aws_package.ts | 2742 +++++++++++++++++ x-pack/plugins/fleet/common/services/index.ts | 15 +- .../fleet/common/services/limited_package.ts | 5 +- .../package_to_package_policy.test.ts | 69 +- .../services/package_to_package_policy.ts | 136 +- .../services/validate_package_policy.test.ts | 635 ++++ .../services/validate_package_policy.ts | 101 +- .../common/types/models/package_policy.ts | 1 + .../fleet/hooks/use_breadcrumbs.tsx | 4 +- .../components/layout.tsx | 18 +- .../create_package_policy_page/index.tsx | 101 +- .../services/index.ts | 2 +- .../services/validate_package_policy.test.ts | 600 ---- .../step_configure_package.tsx | 134 +- .../step_define_package_policy.tsx | 9 +- x-pack/plugins/fleet/public/services/index.ts | 8 + .../fleet/server/saved_objects/index.ts | 1 + .../server/services/package_policy.test.ts | 32 + .../fleet/server/services/package_policy.ts | 10 +- .../server/types/models/package_policy.ts | 1 + 22 files changed, 4910 insertions(+), 819 deletions(-) create mode 100644 x-pack/plugins/fleet/common/services/__snapshots__/package_to_package_policy.test.ts.snap create mode 100644 x-pack/plugins/fleet/common/services/__snapshots__/validate_package_policy.test.ts.snap create mode 100644 x-pack/plugins/fleet/common/services/fixtures/aws_package.ts create mode 100644 x-pack/plugins/fleet/common/services/validate_package_policy.test.ts rename x-pack/plugins/fleet/{public/applications/fleet/sections/agent_policy/create_package_policy_page => common}/services/validate_package_policy.ts (76%) delete mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/services/validate_package_policy.test.ts diff --git a/x-pack/plugins/fleet/common/services/__snapshots__/package_to_package_policy.test.ts.snap b/x-pack/plugins/fleet/common/services/__snapshots__/package_to_package_policy.test.ts.snap new file mode 100644 index 000000000000..d49743fa487f --- /dev/null +++ b/x-pack/plugins/fleet/common/services/__snapshots__/package_to_package_policy.test.ts.snap @@ -0,0 +1,804 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Fleet - packageToPackagePolicy packageToPackagePolicy returns package policy with multiple policy templates (aka has integrations 1`] = ` +Object { + "description": undefined, + "enabled": true, + "inputs": Array [ + Object { + "enabled": true, + "policy_template": "billing", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.billing", + "type": "metrics", + }, + "enabled": true, + "vars": Object { + "cost_explorer_config.group_by_dimension_keys": Object { + "type": "text", + "value": Array [ + "AZ", + "INSTANCE_TYPE", + "SERVICE", + ], + }, + "cost_explorer_config.group_by_tag_keys": Object { + "type": "text", + "value": Array [ + "aws:createdBy", + ], + }, + "latency": Object { + "type": "text", + "value": undefined, + }, + "period": Object { + "type": "text", + "value": "12h", + }, + }, + }, + ], + "type": "aws/metrics", + }, + Object { + "enabled": true, + "policy_template": "cloudtrail", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.cloudtrail", + "type": "logs", + }, + "enabled": true, + "vars": Object { + "api_timeout": Object { + "type": "text", + "value": undefined, + }, + "fips_enabled": Object { + "type": "bool", + "value": false, + }, + "queue_url": Object { + "type": "text", + "value": undefined, + }, + "visibility_timeout": Object { + "type": "text", + "value": undefined, + }, + }, + }, + ], + "type": "s3", + }, + Object { + "enabled": false, + "policy_template": "cloudtrail", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.cloudtrail", + "type": "logs", + }, + "enabled": false, + "vars": Object { + "interval": Object { + "type": "text", + "value": "10s", + }, + "password": Object { + "type": "password", + "value": undefined, + }, + "search": Object { + "type": "text", + "value": "search sourcetype=aws:cloudtrail", + }, + "ssl": Object { + "type": "yaml", + "value": undefined, + }, + "tags": Object { + "type": "text", + "value": Array [ + "forwarded", + ], + }, + "url": Object { + "type": "text", + "value": "https://server.example.com:8089", + }, + "username": Object { + "type": "text", + "value": undefined, + }, + }, + }, + ], + "type": "httpjson", + }, + Object { + "enabled": true, + "policy_template": "cloudwatch", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.cloudwatch_logs", + "type": "logs", + }, + "enabled": true, + "vars": Object { + "api_timeout": Object { + "type": "text", + "value": undefined, + }, + "fips_enabled": Object { + "type": "bool", + "value": false, + }, + "queue_url": Object { + "type": "text", + "value": undefined, + }, + "visibility_timeout": Object { + "type": "text", + "value": undefined, + }, + }, + }, + ], + "type": "s3", + }, + Object { + "enabled": true, + "policy_template": "cloudwatch", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.cloudwatch_metrics", + "type": "metrics", + }, + "enabled": true, + "vars": Object { + "latency": Object { + "type": "text", + "value": undefined, + }, + "metrics": Object { + "type": "yaml", + "value": "- namespace: AWS/EC2 + resource_type: ec2:instance + name: + - CPUUtilization + - DiskWriteOps + statistic: + - Average + - Maximum + # dimensions: + # - name: InstanceId + # value: i-123456 + # tags: + # - key: created-by + # value: foo +", + }, + "period": Object { + "type": "text", + "value": "300s", + }, + "regions": Object { + "type": "text", + "value": Array [], + }, + }, + }, + ], + "type": "aws/metrics", + }, + Object { + "enabled": true, + "policy_template": "dynamodb", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.dynamodb", + "type": "metrics", + }, + "enabled": true, + "vars": Object { + "latency": Object { + "type": "text", + "value": undefined, + }, + "period": Object { + "type": "text", + "value": "5m", + }, + "regions": Object { + "type": "text", + "value": Array [], + }, + "tags_filter": Object { + "type": "yaml", + "value": "# - key: \\"created-by\\" + # value: \\"foo\\" +", + }, + }, + }, + ], + "type": "aws/metrics", + }, + Object { + "enabled": true, + "policy_template": "ebs", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.ebs", + "type": "metrics", + }, + "enabled": true, + "vars": Object { + "latency": Object { + "type": "text", + "value": undefined, + }, + "period": Object { + "type": "text", + "value": "5m", + }, + "regions": Object { + "type": "text", + "value": Array [], + }, + "tags_filter": Object { + "type": "yaml", + "value": "# - key: \\"created-by\\" + # value: \\"foo\\" +", + }, + }, + }, + ], + "type": "aws/metrics", + }, + Object { + "enabled": true, + "policy_template": "ec2", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.ec2_logs", + "type": "logs", + }, + "enabled": true, + "vars": Object { + "api_timeout": Object { + "type": "text", + "value": undefined, + }, + "fips_enabled": Object { + "type": "bool", + "value": false, + }, + "queue_url": Object { + "type": "text", + "value": undefined, + }, + "visibility_timeout": Object { + "type": "text", + "value": undefined, + }, + }, + }, + ], + "type": "s3", + }, + Object { + "enabled": true, + "policy_template": "ec2", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.ec2_metrics", + "type": "metrics", + }, + "enabled": true, + "vars": Object { + "latency": Object { + "type": "text", + "value": undefined, + }, + "period": Object { + "type": "text", + "value": "5m", + }, + "regions": Object { + "type": "text", + "value": Array [], + }, + "tags_filter": Object { + "type": "yaml", + "value": "# - key: \\"created-by\\" + # value: \\"foo\\" +", + }, + }, + }, + ], + "type": "aws/metrics", + }, + Object { + "enabled": true, + "policy_template": "elb", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.elb_logs", + "type": "logs", + }, + "enabled": true, + "vars": Object { + "api_timeout": Object { + "type": "text", + "value": undefined, + }, + "fips_enabled": Object { + "type": "bool", + "value": false, + }, + "queue_url": Object { + "type": "text", + "value": undefined, + }, + "visibility_timeout": Object { + "type": "text", + "value": undefined, + }, + }, + }, + ], + "type": "s3", + }, + Object { + "enabled": true, + "policy_template": "elb", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.elb_metrics", + "type": "metrics", + }, + "enabled": true, + "vars": Object { + "latency": Object { + "type": "text", + "value": undefined, + }, + "period": Object { + "type": "text", + "value": "1m", + }, + "regions": Object { + "type": "text", + "value": Array [], + }, + "tags_filter": Object { + "type": "yaml", + "value": "# - key: \\"created-by\\" + # value: \\"foo\\" +", + }, + }, + }, + ], + "type": "aws/metrics", + }, + Object { + "enabled": true, + "policy_template": "lambda", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.lambda", + "type": "metrics", + }, + "enabled": true, + "vars": Object { + "latency": Object { + "type": "text", + "value": undefined, + }, + "period": Object { + "type": "text", + "value": "5m", + }, + "regions": Object { + "type": "text", + "value": Array [], + }, + "tags_filter": Object { + "type": "yaml", + "value": "# - key: \\"created-by\\" + # value: \\"foo\\" +", + }, + }, + }, + ], + "type": "aws/metrics", + }, + Object { + "enabled": true, + "policy_template": "natgateway", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.natgateway", + "type": "metrics", + }, + "enabled": true, + "vars": Object { + "latency": Object { + "type": "text", + "value": undefined, + }, + "period": Object { + "type": "text", + "value": "1m", + }, + "regions": Object { + "type": "text", + "value": Array [], + }, + }, + }, + ], + "type": "aws/metrics", + }, + Object { + "enabled": true, + "policy_template": "rds", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.rds", + "type": "metrics", + }, + "enabled": true, + "vars": Object { + "latency": Object { + "type": "text", + "value": undefined, + }, + "period": Object { + "type": "text", + "value": "1m", + }, + "regions": Object { + "type": "text", + "value": Array [], + }, + "tags_filter": Object { + "type": "yaml", + "value": "# - key: \\"created-by\\" + # value: \\"foo\\" +", + }, + }, + }, + ], + "type": "aws/metrics", + }, + Object { + "enabled": true, + "policy_template": "s3", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.s3access", + "type": "logs", + }, + "enabled": true, + "vars": Object { + "api_timeout": Object { + "type": "text", + "value": undefined, + }, + "fips_enabled": Object { + "type": "bool", + "value": false, + }, + "queue_url": Object { + "type": "text", + "value": undefined, + }, + "visibility_timeout": Object { + "type": "text", + "value": undefined, + }, + }, + }, + ], + "type": "s3", + }, + Object { + "enabled": true, + "policy_template": "s3", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.s3_daily_storage", + "type": "metrics", + }, + "enabled": true, + "vars": Object { + "latency": Object { + "type": "text", + "value": undefined, + }, + "period": Object { + "type": "text", + "value": "24h", + }, + "regions": Object { + "type": "text", + "value": Array [], + }, + }, + }, + Object { + "data_stream": Object { + "dataset": "aws.s3_request", + "type": "metrics", + }, + "enabled": true, + "vars": Object { + "latency": Object { + "type": "text", + "value": undefined, + }, + "period": Object { + "type": "text", + "value": "1m", + }, + "regions": Object { + "type": "text", + "value": Array [], + }, + }, + }, + ], + "type": "aws/metrics", + }, + Object { + "enabled": true, + "policy_template": "sns", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.sns", + "type": "metrics", + }, + "enabled": true, + "vars": Object { + "latency": Object { + "type": "text", + "value": undefined, + }, + "period": Object { + "type": "text", + "value": "5m", + }, + "regions": Object { + "type": "text", + "value": Array [], + }, + "tags_filter": Object { + "type": "yaml", + "value": "# - key: \\"created-by\\" + # value: \\"foo\\" +", + }, + }, + }, + ], + "type": "aws/metrics", + }, + Object { + "enabled": true, + "policy_template": "sqs", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.sqs", + "type": "metrics", + }, + "enabled": true, + "vars": Object { + "latency": Object { + "type": "text", + "value": undefined, + }, + "period": Object { + "type": "text", + "value": "5m", + }, + "regions": Object { + "type": "text", + "value": Array [], + }, + }, + }, + ], + "type": "aws/metrics", + }, + Object { + "enabled": true, + "policy_template": "transitgateway", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.transitgateway", + "type": "metrics", + }, + "enabled": true, + "vars": Object { + "latency": Object { + "type": "text", + "value": undefined, + }, + "period": Object { + "type": "text", + "value": "1m", + }, + "regions": Object { + "type": "text", + "value": Array [], + }, + }, + }, + ], + "type": "aws/metrics", + }, + Object { + "enabled": true, + "policy_template": "usage", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.usage", + "type": "metrics", + }, + "enabled": true, + "vars": Object { + "latency": Object { + "type": "text", + "value": undefined, + }, + "period": Object { + "type": "text", + "value": "1m", + }, + "regions": Object { + "type": "text", + "value": Array [], + }, + }, + }, + ], + "type": "aws/metrics", + }, + Object { + "enabled": true, + "policy_template": "vpcflow", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.vpcflow", + "type": "logs", + }, + "enabled": true, + "vars": Object { + "api_timeout": Object { + "type": "text", + "value": undefined, + }, + "fips_enabled": Object { + "type": "bool", + "value": false, + }, + "queue_url": Object { + "type": "text", + "value": undefined, + }, + "visibility_timeout": Object { + "type": "text", + "value": undefined, + }, + }, + }, + ], + "type": "s3", + }, + Object { + "enabled": true, + "policy_template": "vpn", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "aws.vpn", + "type": "metrics", + }, + "enabled": true, + "vars": Object { + "latency": Object { + "type": "text", + "value": undefined, + }, + "period": Object { + "type": "text", + "value": "1m", + }, + "regions": Object { + "type": "text", + "value": Array [], + }, + "tags_filter": Object { + "type": "yaml", + "value": "# - key: \\"created-by\\" + # value: \\"foo\\" +", + }, + }, + }, + ], + "type": "aws/metrics", + }, + ], + "name": "aws-1", + "namespace": "default", + "output_id": "some-output-id", + "package": Object { + "name": "aws", + "title": "AWS", + "version": "0.5.3", + }, + "policy_id": "some-agent-policy-id", + "vars": Object { + "access_key_id": Object { + "type": "text", + "value": undefined, + }, + "credential_profile_name": Object { + "type": "text", + "value": undefined, + }, + "endpoint": Object { + "type": "text", + "value": "amazonaws.com", + }, + "role_arn": Object { + "type": "text", + "value": undefined, + }, + "secret_access_key": Object { + "type": "text", + "value": undefined, + }, + "session_token": Object { + "type": "text", + "value": undefined, + }, + "shared_credential_file": Object { + "type": "text", + "value": undefined, + }, + }, +} +`; diff --git a/x-pack/plugins/fleet/common/services/__snapshots__/validate_package_policy.test.ts.snap b/x-pack/plugins/fleet/common/services/__snapshots__/validate_package_policy.test.ts.snap new file mode 100644 index 000000000000..72f4182d8762 --- /dev/null +++ b/x-pack/plugins/fleet/common/services/__snapshots__/validate_package_policy.test.ts.snap @@ -0,0 +1,301 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Fleet - validatePackagePolicy() works for packages with multiple policy templates (aka integrations) returns errors for invalid package policy 1`] = ` +Object { + "description": null, + "inputs": Object { + "billing-aws/metrics": Object { + "streams": Object { + "aws.billing": Object { + "vars": Object { + "cost_explorer_config.group_by_dimension_keys": null, + "cost_explorer_config.group_by_tag_keys": null, + "latency": null, + "period": null, + }, + }, + }, + }, + "cloudtrail-httpjson": Object { + "streams": Object { + "aws.cloudtrail": Object { + "vars": Object { + "interval": null, + "password": null, + "search": null, + "ssl": null, + "tags": null, + "url": null, + "username": null, + }, + }, + }, + }, + "cloudtrail-s3": Object { + "streams": Object { + "aws.cloudtrail": Object { + "vars": Object { + "api_timeout": null, + "fips_enabled": null, + "queue_url": Array [ + "Queue URL is required", + ], + "visibility_timeout": null, + }, + }, + }, + }, + "cloudwatch-aws/metrics": Object { + "streams": Object { + "aws.cloudwatch_metrics": Object { + "vars": Object { + "latency": null, + "metrics": null, + "period": null, + "regions": null, + }, + }, + }, + }, + "cloudwatch-s3": Object { + "streams": Object { + "aws.cloudwatch_logs": Object { + "vars": Object { + "api_timeout": null, + "fips_enabled": null, + "queue_url": Array [ + "Queue URL is required", + ], + "visibility_timeout": null, + }, + }, + }, + }, + "dynamodb-aws/metrics": Object { + "streams": Object { + "aws.dynamodb": Object { + "vars": Object { + "latency": null, + "period": null, + "regions": null, + "tags_filter": null, + }, + }, + }, + }, + "ebs-aws/metrics": Object { + "streams": Object { + "aws.ebs": Object { + "vars": Object { + "latency": null, + "period": null, + "regions": null, + "tags_filter": null, + }, + }, + }, + }, + "ec2-aws/metrics": Object { + "streams": Object { + "aws.ec2_metrics": Object { + "vars": Object { + "latency": null, + "period": null, + "regions": null, + "tags_filter": null, + }, + }, + }, + }, + "ec2-s3": Object { + "streams": Object { + "aws.ec2_logs": Object { + "vars": Object { + "api_timeout": null, + "fips_enabled": null, + "queue_url": Array [ + "Queue URL is required", + ], + "visibility_timeout": null, + }, + }, + }, + }, + "elb-aws/metrics": Object { + "streams": Object { + "aws.elb_metrics": Object { + "vars": Object { + "latency": null, + "period": null, + "regions": null, + "tags_filter": null, + }, + }, + }, + }, + "elb-s3": Object { + "streams": Object { + "aws.elb_logs": Object { + "vars": Object { + "api_timeout": null, + "fips_enabled": null, + "queue_url": Array [ + "Queue URL is required", + ], + "visibility_timeout": null, + }, + }, + }, + }, + "lambda-aws/metrics": Object { + "streams": Object { + "aws.lambda": Object { + "vars": Object { + "latency": null, + "period": null, + "regions": null, + "tags_filter": null, + }, + }, + }, + }, + "natgateway-aws/metrics": Object { + "streams": Object { + "aws.natgateway": Object { + "vars": Object { + "latency": null, + "period": null, + "regions": null, + }, + }, + }, + }, + "rds-aws/metrics": Object { + "streams": Object { + "aws.rds": Object { + "vars": Object { + "latency": null, + "period": null, + "regions": null, + "tags_filter": null, + }, + }, + }, + }, + "s3-aws/metrics": Object { + "streams": Object { + "aws.s3_daily_storage": Object { + "vars": Object { + "latency": null, + "period": null, + "regions": null, + }, + }, + "aws.s3_request": Object { + "vars": Object { + "latency": null, + "period": null, + "regions": null, + }, + }, + }, + }, + "s3-s3": Object { + "streams": Object { + "aws.s3access": Object { + "vars": Object { + "api_timeout": null, + "fips_enabled": null, + "queue_url": Array [ + "Queue URL is required", + ], + "visibility_timeout": null, + }, + }, + }, + }, + "sns-aws/metrics": Object { + "streams": Object { + "aws.sns": Object { + "vars": Object { + "latency": null, + "period": null, + "regions": null, + "tags_filter": null, + }, + }, + }, + }, + "sqs-aws/metrics": Object { + "streams": Object { + "aws.sqs": Object { + "vars": Object { + "latency": null, + "period": null, + "regions": null, + }, + }, + }, + }, + "transitgateway-aws/metrics": Object { + "streams": Object { + "aws.transitgateway": Object { + "vars": Object { + "latency": null, + "period": null, + "regions": null, + }, + }, + }, + }, + "usage-aws/metrics": Object { + "streams": Object { + "aws.usage": Object { + "vars": Object { + "latency": null, + "period": null, + "regions": null, + }, + }, + }, + }, + "vpcflow-s3": Object { + "streams": Object { + "aws.vpcflow": Object { + "vars": Object { + "api_timeout": null, + "fips_enabled": null, + "queue_url": Array [ + "Queue URL is required", + ], + "visibility_timeout": null, + }, + }, + }, + }, + "vpn-aws/metrics": Object { + "streams": Object { + "aws.vpn": Object { + "vars": Object { + "latency": null, + "period": null, + "regions": null, + "tags_filter": null, + }, + }, + }, + }, + }, + "name": null, + "namespace": null, + "vars": Object { + "access_key_id": null, + "credential_profile_name": null, + "endpoint": null, + "role_arn": null, + "secret_access_key": null, + "session_token": null, + "shared_credential_file": null, + }, +} +`; diff --git a/x-pack/plugins/fleet/common/services/fixtures/aws_package.ts b/x-pack/plugins/fleet/common/services/fixtures/aws_package.ts new file mode 100644 index 000000000000..2b93cca3d4e4 --- /dev/null +++ b/x-pack/plugins/fleet/common/services/fixtures/aws_package.ts @@ -0,0 +1,2742 @@ +/* + * 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 const AWS_PACKAGE = { + name: 'aws', + title: 'AWS', + version: '0.5.3', + release: 'beta', + description: 'AWS Integration', + type: 'integration', + download: '/epr/aws/aws-0.5.3.zip', + path: '/package/aws/0.5.3', + icons: [ + { + src: '/img/logo_aws.svg', + path: '/package/aws/0.5.3/img/logo_aws.svg', + title: 'logo aws', + size: '32x32', + type: 'image/svg+xml', + }, + ], + format_version: '1.0.0', + readme: '/package/aws/0.5.3/docs/README.md', + license: 'basic', + categories: ['aws', 'cloud', 'network', 'security'], + conditions: { + 'kibana.version': '^7.12.0', + }, + screenshots: [ + { + src: '/img/metricbeat-aws-overview.png', + path: '/package/aws/0.5.3/img/metricbeat-aws-overview.png', + title: 'metricbeat aws overview', + size: '3848x2440', + type: 'image/png', + }, + ], + assets: { + kibana: { + dashboard: [], + map: [], + search: [], + visualization: [], + }, + elasticsearch: { + ingest_pipeline: [], + }, + }, + policy_templates: [ + { + name: 'billing', + title: 'AWS Billing', + description: 'Collect AWS billing metrics', + data_streams: ['billing'], + inputs: [ + { + type: 'aws/metrics', + title: 'Collect billing metrics', + description: 'Collect billing metrics', + input_group: 'metrics', + }, + ], + multiple: true, + icons: [ + { + src: '/img/logo_billing.svg', + path: '/package/aws/0.5.3/img/logo_billing.svg', + title: 'AWS Billing logo', + size: '32x32', + type: 'image/svg+xml', + }, + ], + screenshots: [ + { + src: '/img/metricbeat-aws-billing-overview.png', + path: '/package/aws/0.5.3/img/metricbeat-aws-billing-overview.png', + title: 'metricbeat aws billing overview', + size: '2640x2240', + type: 'image/png', + }, + ], + readme: '/package/aws/0.5.3/docs/billing.md', + }, + { + name: 'cloudtrail', + title: 'AWS Cloudtrail', + description: 'Collect logs from AWS Cloudtrail', + data_streams: ['cloudtrail'], + inputs: [ + { + type: 's3', + title: 'Collect logs from Cloudtrail service', + description: 'Collecting Cloudtrail logs using S3 input', + input_group: 'logs', + }, + { + type: 'httpjson', + title: 'Collect logs from third-party REST API (experimental)', + description: 'Collect logs from third-party REST API (experimental)', + input_group: 'logs', + }, + ], + multiple: true, + icons: [ + { + src: '/img/logo_cloudtrail.svg', + path: '/package/aws/0.5.3/img/logo_cloudtrail.svg', + title: 'AWS Cloudtrail logo', + size: '32x32', + type: 'image/svg+xml', + }, + ], + screenshots: [ + { + src: '/img/filebeat-aws-cloudtrail.png', + path: '/package/aws/0.5.3/img/filebeat-aws-cloudtrail.png', + title: 'filebeat aws cloudtrail', + size: '1702x1063', + type: 'image/png', + }, + ], + readme: '/package/aws/0.5.3/docs/cloudtrail.md', + }, + { + name: 'cloudwatch', + title: 'AWS CloudWatch', + description: 'Collect logs and metrics from CloudWatch', + data_streams: ['cloudwatch_logs', 'cloudwatch_metrics'], + inputs: [ + { + type: 's3', + title: 'Collect logs from CloudWatch', + description: 'Collecting logs from CloudWatch using S3 input', + input_group: 'logs', + }, + { + type: 'aws/metrics', + title: 'Collect metrics from CloudWatch', + description: 'Collecting metrics from AWS CloudWatch', + input_group: 'metrics', + }, + ], + multiple: true, + icons: [ + { + src: '/img/logo_cloudwatch.svg', + path: '/package/aws/0.5.3/img/logo_cloudwatch.svg', + title: 'AWS CloudWatch logo', + size: '32x32', + type: 'image/svg+xml', + }, + ], + readme: '/package/aws/0.5.3/docs/cloudwatch.md', + }, + { + name: 'dynamodb', + title: 'AWS DynamoDB', + description: 'Collect AWS DynamoDB metrics', + data_streams: ['dynamodb'], + inputs: [ + { + type: 'aws/metrics', + title: 'Collect dynamodb metrics', + description: 'Collect dynamodb metrics', + input_group: 'metrics', + }, + ], + multiple: true, + icons: [ + { + src: '/img/logo_dynamodb.svg', + path: '/package/aws/0.5.3/img/logo_dynamodb.svg', + title: 'AWS DynamoDB logo', + size: '32x32', + type: 'image/svg+xml', + }, + ], + categories: ['datastore'], + screenshots: [ + { + src: '/img/metricbeat-aws-dynamodb-overview.png', + path: '/package/aws/0.5.3/img/metricbeat-aws-dynamodb-overview.png', + title: 'metricbeat aws dynamodb overview', + size: '2640x2240', + type: 'image/png', + }, + ], + readme: '/package/aws/0.5.3/docs/dynamodb.md', + }, + { + name: 'ebs', + title: 'AWS EBS', + description: 'Collect AWS EBS metrics', + data_streams: ['ebs'], + inputs: [ + { + type: 'aws/metrics', + title: 'Collect EBS metrics', + description: 'Collect EBS metrics', + input_group: 'metrics', + }, + ], + multiple: true, + icons: [ + { + src: '/img/logo_ebs.svg', + path: '/package/aws/0.5.3/img/logo_ebs.svg', + title: 'AWS EBS logo', + size: '32x32', + type: 'image/svg+xml', + }, + ], + categories: ['datastore'], + screenshots: [ + { + src: '/img/metricbeat-aws-ebs-overview.png', + path: '/package/aws/0.5.3/img/metricbeat-aws-ebs-overview.png', + title: 'metricbeat aws ebs overview', + size: '2640x2240', + type: 'image/png', + }, + ], + readme: '/package/aws/0.5.3/docs/ebs.md', + }, + { + name: 'ec2', + title: 'AWS EC2', + description: 'Collect logs and metrics from EC2 service', + data_streams: ['ec2_logs', 'ec2_metrics'], + inputs: [ + { + type: 's3', + title: 'Collect logs from EC2 service', + description: 'Collecting EC2 logs using S3 input', + input_group: 'logs', + }, + { + type: 'aws/metrics', + title: 'Collect metrics from EC2 service', + description: 'Collecting EC2 metrics using AWS CloudWatch', + input_group: 'metrics', + }, + ], + multiple: true, + icons: [ + { + src: '/img/logo_ec2.svg', + path: '/package/aws/0.5.3/img/logo_ec2.svg', + title: 'AWS EC2 logo', + size: '32x32', + type: 'image/svg+xml', + }, + ], + screenshots: [ + { + src: '/img/metricbeat-aws-ec2-overview.png', + path: '/package/aws/0.5.3/img/metricbeat-aws-ec2-overview.png', + title: 'metricbeat aws ec2 overview', + size: '2640x2240', + type: 'image/png', + }, + ], + readme: '/package/aws/0.5.3/docs/ec2.md', + }, + { + name: 'elb', + title: 'AWS ELB', + description: 'Collect logs and metrics from ELB service', + data_streams: ['elb_logs', 'elb_metrics'], + inputs: [ + { + type: 's3', + title: 'Collect logs from ELB service', + description: 'Collecting ELB logs using S3 input', + input_group: 'logs', + }, + { + type: 'aws/metrics', + title: 'Collect metrics from ELB service', + description: 'Collecting ELB metrics using AWS CloudWatch', + input_group: 'metrics', + }, + ], + multiple: true, + icons: [ + { + src: '/img/logo_elb.svg', + path: '/package/aws/0.5.3/img/logo_elb.svg', + title: 'AWS ELB logo', + size: '32x32', + type: 'image/svg+xml', + }, + ], + screenshots: [ + { + src: '/img/metricbeat-aws-elb-overview.png', + path: '/package/aws/0.5.3/img/metricbeat-aws-elb-overview.png', + title: 'metricbeat aws elb overview', + size: '2640x2240', + type: 'image/png', + }, + { + src: '/img/filebeat-aws-elb-overview.png', + path: '/package/aws/0.5.3/img/filebeat-aws-elb-overview.png', + title: 'filebeat aws elb overview', + size: '1684x897', + type: 'image/png', + }, + ], + readme: '/package/aws/0.5.3/docs/elb.md', + }, + { + name: 'lambda', + title: 'AWS Lambda', + description: 'Collect AWS Lambda metrics', + data_streams: ['lambda'], + inputs: [ + { + type: 'aws/metrics', + title: 'Collect Lambda metrics', + description: 'Collect Lambda metrics', + input_group: 'metrics', + }, + ], + multiple: true, + icons: [ + { + src: '/img/logo_lambda.svg', + path: '/package/aws/0.5.3/img/logo_lambda.svg', + title: 'AWS Lambda logo', + size: '32x32', + type: 'image/svg+xml', + }, + ], + screenshots: [ + { + src: '/img/metricbeat-aws-lambda-overview.png', + path: '/package/aws/0.5.3/img/metricbeat-aws-lambda-overview.png', + title: 'metricbeat aws lambda overview', + size: '2640x2240', + type: 'image/png', + }, + ], + readme: '/package/aws/0.5.3/docs/lambda.md', + }, + { + name: 'natgateway', + title: 'AWS NATGateway', + description: 'Collect AWS NATGateway metrics', + data_streams: ['natgateway'], + inputs: [ + { + type: 'aws/metrics', + title: 'Collect NATGateway metrics', + description: 'Collect NATGateway metrics', + input_group: 'metrics', + }, + ], + multiple: true, + icons: [ + { + src: '/img/logo_natgateway.svg', + path: '/package/aws/0.5.3/img/logo_natgateway.svg', + title: 'AWS NATGateway logo', + size: '32x32', + type: 'image/svg+xml', + }, + ], + readme: '/package/aws/0.5.3/docs/natgateway.md', + }, + { + name: 'rds', + title: 'AWS RDS', + description: 'Collect AWS RDS metrics', + data_streams: ['rds'], + inputs: [ + { + type: 'aws/metrics', + title: 'Collect RDS metrics', + description: 'Collect RDS metrics', + input_group: 'metrics', + }, + ], + multiple: true, + icons: [ + { + src: '/img/logo_rds.svg', + path: '/package/aws/0.5.3/img/logo_rds.svg', + title: 'AWS RDS logo', + size: '32x32', + type: 'image/svg+xml', + }, + ], + categories: ['datastore'], + screenshots: [ + { + src: '/img/metricbeat-aws-rds-overview.png', + path: '/package/aws/0.5.3/img/metricbeat-aws-rds-overview.png', + title: 'metricbeat aws rds overview', + size: '2640x2240', + type: 'image/png', + }, + ], + readme: '/package/aws/0.5.3/docs/rds.md', + }, + { + name: 's3', + title: 'AWS S3', + description: 'Collect AWS S3 metrics', + data_streams: ['s3_daily_storage', 's3_request', 's3access'], + inputs: [ + { + type: 's3', + title: 'Collect S3 access logs', + description: 'Collecting S3 access logs using S3 input', + input_group: 'logs', + }, + { + type: 'aws/metrics', + title: 'Collect metrics from S3', + description: 'Collecting S3 metrics using AWS CloudWatch', + input_group: 'metrics', + }, + ], + multiple: true, + icons: [ + { + src: '/img/logo_s3.svg', + path: '/package/aws/0.5.3/img/logo_s3.svg', + title: 'AWS S3 logo', + size: '32x32', + type: 'image/svg+xml', + }, + ], + categories: ['datastore'], + screenshots: [ + { + src: '/img/metricbeat-aws-s3-overview.png', + path: '/package/aws/0.5.3/img/metricbeat-aws-s3-overview.png', + title: 'metricbeat aws s3 overview', + size: '2640x2240', + type: 'image/png', + }, + { + src: '/img/filebeat-aws-s3access-overview.png', + path: '/package/aws/0.5.3/img/filebeat-aws-s3access-overview.png', + title: 'filebeat aws s3access overview', + size: '1684x897', + type: 'image/png', + }, + ], + readme: '/package/aws/0.5.3/docs/s3.md', + }, + { + name: 'sns', + title: 'AWS SNS', + description: 'Collect AWS SNS metrics', + data_streams: ['sns'], + inputs: [ + { + type: 'aws/metrics', + title: 'Collect SNS metrics', + description: 'Collect SNS metrics', + input_group: 'metrics', + }, + ], + multiple: true, + icons: [ + { + src: '/img/logo_sns.svg', + path: '/package/aws/0.5.3/img/logo_sns.svg', + title: 'AWS SNS logo', + size: '32x32', + type: 'image/svg+xml', + }, + ], + screenshots: [ + { + src: '/img/metricbeat-aws-sns-overview.png', + path: '/package/aws/0.5.3/img/metricbeat-aws-sns-overview.png', + title: 'metricbeat aws sns overview', + size: '2640x2240', + type: 'image/png', + }, + ], + readme: '/package/aws/0.5.3/docs/sns.md', + }, + { + name: 'sqs', + title: 'AWS SQS', + description: 'Collect AWS SQS metrics', + data_streams: ['sqs'], + inputs: [ + { + type: 'aws/metrics', + title: 'Collect SQS metrics', + description: 'Collect SQS metrics', + input_group: 'metrics', + }, + ], + multiple: true, + icons: [ + { + src: '/img/logo_sqs.svg', + path: '/package/aws/0.5.3/img/logo_sqs.svg', + title: 'AWS SQS logo', + size: '32x32', + type: 'image/svg+xml', + }, + ], + screenshots: [ + { + src: '/img/metricbeat-aws-sqs-overview.png', + path: '/package/aws/0.5.3/img/metricbeat-aws-sqs-overview.png', + title: 'metricbeat aws sqs overview', + size: '2640x2240', + type: 'image/png', + }, + ], + readme: '/package/aws/0.5.3/docs/sqs.md', + }, + { + name: 'transitgateway', + title: 'AWS Transit Gateway', + description: 'Collect AWS Transit Gateway metrics', + data_streams: ['transitgateway'], + inputs: [ + { + type: 'aws/metrics', + title: 'Collect Transit Gateway metrics', + description: 'Collect Transit Gateway metrics', + input_group: 'metrics', + }, + ], + multiple: true, + icons: [ + { + src: '/img/logo_transitgateway.svg', + path: '/package/aws/0.5.3/img/logo_transitgateway.svg', + title: 'AWS Transit Gateway logo', + size: '32x32', + type: 'image/svg+xml', + }, + ], + readme: '/package/aws/0.5.3/docs/transitgateway.md', + }, + { + name: 'usage', + title: 'AWS Usage', + description: 'Collect AWS Usage metrics', + data_streams: ['usage'], + inputs: [ + { + type: 'aws/metrics', + title: 'Collect Usage metrics', + description: 'Collect Usage metrics', + input_group: 'metrics', + }, + ], + multiple: true, + screenshots: [ + { + src: '/img/metricbeat-aws-usage-overview.png', + path: '/package/aws/0.5.3/img/metricbeat-aws-usage-overview.png', + title: 'metricbeat aws sns overview', + size: '2640x2240', + type: 'image/png', + }, + ], + readme: '/package/aws/0.5.3/docs/usage.md', + }, + { + name: 'vpcflow', + title: 'AWS VPC Flow', + description: 'Collect AWS vpcflow logs', + data_streams: ['vpcflow'], + inputs: [ + { + type: 's3', + title: 'Collect VPC Flow logs', + description: 'Collecting VPC Flow logs using S3 input', + input_group: 'logs', + }, + ], + multiple: true, + icons: [ + { + src: '/img/logo_vpcflow.svg', + path: '/package/aws/0.5.3/img/logo_vpcflow.svg', + title: 'AWS VPC logo', + size: '32x32', + type: 'image/svg+xml', + }, + ], + readme: '/package/aws/0.5.3/docs/vpcflow.md', + }, + { + name: 'vpn', + title: 'AWS VPN', + description: 'Collect AWS VPN metrics', + data_streams: ['vpn'], + inputs: [ + { + type: 'aws/metrics', + title: 'Collect VPN metrics', + description: 'Collect VPN metrics', + input_group: 'metrics', + }, + ], + multiple: true, + icons: [ + { + src: '/img/logo_vpn.svg', + path: '/package/aws/0.5.3/img/logo_vpn.svg', + title: 'AWS VPN logo', + size: '32x32', + type: 'image/svg+xml', + }, + ], + categories: ['network'], + readme: '/package/aws/0.5.3/docs/vpn.md', + }, + ], + data_streams: [ + { + type: 'metrics', + dataset: 'aws.billing', + title: 'AWS billing metrics', + release: 'beta', + streams: [ + { + input: 'aws/metrics', + vars: [ + { + name: 'period', + type: 'text', + title: 'Period', + multi: false, + required: true, + show_user: true, + default: '12h', + }, + { + name: 'latency', + type: 'text', + title: 'Latency', + multi: false, + required: false, + show_user: false, + }, + { + name: 'cost_explorer_config.group_by_dimension_keys', + type: 'text', + title: 'Cost Explorer Group By Dimension Keys', + multi: true, + required: false, + show_user: true, + default: ['AZ', 'INSTANCE_TYPE', 'SERVICE'], + }, + { + name: 'cost_explorer_config.group_by_tag_keys', + type: 'text', + title: 'Cost Explorer Group By Tag Keys', + multi: true, + required: false, + show_user: true, + default: ['aws:createdBy'], + }, + ], + template_path: 'stream.yml.hbs', + title: 'AWS Billing metrics', + description: 'Collect AWS billing metrics', + enabled: true, + }, + ], + package: 'aws', + path: 'billing', + }, + { + type: 'logs', + dataset: 'aws.cloudtrail', + title: 'AWS CloudTrail logs', + release: 'beta', + ingest_pipeline: 'default', + streams: [ + { + input: 's3', + vars: [ + { + name: 'visibility_timeout', + type: 'text', + title: 'Visibility Timeout', + description: + 'The duration that the received messages are hidden from subsequent retrieve requests after being retrieved by a ReceiveMessage request. The maximum is 12 hours.', + multi: false, + required: false, + show_user: false, + }, + { + name: 'api_timeout', + type: 'text', + title: 'API Timeout', + description: + 'The maximum duration of AWS API can take. The maximum is half of the visibility timeout value.', + multi: false, + required: false, + show_user: false, + }, + { + name: 'queue_url', + type: 'text', + title: 'Queue URL', + description: 'URL of the AWS SQS queue that messages will be received from.', + multi: false, + required: true, + show_user: true, + }, + { + name: 'fips_enabled', + type: 'bool', + title: 'Enable S3 FIPS', + description: + 'Enabling this option changes the service name from `s3` to `s3-fips` for connecting to the correct service endpoint.', + multi: false, + required: false, + show_user: false, + default: false, + }, + ], + template_path: 's3.yml.hbs', + title: 'AWS CloudTrail logs', + description: 'Collect AWS CloudTrail logs using s3 input', + enabled: true, + }, + { + input: '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: true, + show_user: true, + }, + { + name: 'password', + type: 'password', + title: 'Splunk REST API Password', + multi: false, + required: true, + 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, + }, + { + 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=aws:cloudtrail', + }, + { + name: 'tags', + type: 'text', + title: 'Tags', + multi: true, + required: false, + show_user: false, + default: ['forwarded'], + }, + ], + template_path: 'httpjson.yml.hbs', + title: 'AWS CloudTrail logs via Splunk Enterprise REST API', + description: 'Collect AWS CloudTrail logs via Splunk Enterprise REST API', + enabled: false, + }, + ], + package: 'aws', + path: 'cloudtrail', + }, + { + type: 'logs', + dataset: 'aws.cloudwatch_logs', + title: 'AWS CloudWatch logs', + release: 'beta', + ingest_pipeline: 'default', + streams: [ + { + input: 's3', + vars: [ + { + name: 'visibility_timeout', + type: 'text', + title: 'Visibility Timeout', + description: + 'The duration that the received messages are hidden from subsequent retrieve requests after being retrieved by a ReceiveMessage request. The maximum is 12 hours.', + multi: false, + required: false, + show_user: false, + }, + { + name: 'api_timeout', + type: 'text', + title: 'API Timeout', + description: + 'The maximum duration of AWS API can take. The maximum is half of the visibility timeout value.', + multi: false, + required: false, + show_user: false, + }, + { + name: 'queue_url', + type: 'text', + title: 'Queue URL', + description: 'URL of the AWS SQS queue that messages will be received from.', + multi: false, + required: true, + show_user: true, + }, + { + name: 'fips_enabled', + type: 'bool', + title: 'Enable S3 FIPS', + description: + 'Enabling this option changes the service name from `s3` to `s3-fips` for connecting to the correct service endpoint.', + multi: false, + required: false, + show_user: false, + default: false, + }, + ], + template_path: 's3.yml.hbs', + title: 'AWS CloudWatch logs', + description: 'Collect AWS CloudWatch logs using s3 input', + enabled: true, + }, + ], + package: 'aws', + path: 'cloudwatch_logs', + }, + { + type: 'metrics', + dataset: 'aws.cloudwatch_metrics', + title: 'AWS CloudWatch metrics', + release: 'beta', + streams: [ + { + input: 'aws/metrics', + vars: [ + { + name: 'period', + type: 'text', + title: 'Period', + multi: false, + required: true, + show_user: true, + default: '300s', + }, + { + name: 'regions', + type: 'text', + title: 'Regions', + multi: true, + required: false, + show_user: true, + }, + { + name: 'latency', + type: 'text', + title: 'Latency', + multi: false, + required: false, + show_user: false, + }, + { + name: 'metrics', + type: 'yaml', + title: 'Metrics', + multi: false, + required: true, + show_user: true, + default: + '- namespace: AWS/EC2\n resource_type: ec2:instance\n name:\n - CPUUtilization\n - DiskWriteOps\n statistic:\n - Average\n - Maximum\n # dimensions:\n # - name: InstanceId\n # value: i-123456\n # tags:\n # - key: created-by\n # value: foo\n', + }, + ], + template_path: 'stream.yml.hbs', + title: 'AWS CloudWatch metrics', + description: 'Collect AWS CloudWatch metrics', + enabled: true, + }, + ], + package: 'aws', + path: 'cloudwatch_metrics', + }, + { + type: 'metrics', + dataset: 'aws.dynamodb', + title: 'AWS DynamoDB metrics', + release: 'beta', + streams: [ + { + input: 'aws/metrics', + vars: [ + { + name: 'period', + type: 'text', + title: 'Period', + multi: false, + required: true, + show_user: true, + default: '5m', + }, + { + name: 'regions', + type: 'text', + title: 'Regions', + multi: true, + required: false, + show_user: true, + }, + { + name: 'latency', + type: 'text', + title: 'Latency', + multi: false, + required: false, + show_user: false, + }, + { + name: 'tags_filter', + type: 'yaml', + title: 'Tags Filter', + multi: false, + required: false, + show_user: false, + default: '# - key: "created-by"\n # value: "foo"\n', + }, + ], + template_path: 'stream.yml.hbs', + title: 'AWS DynamoDB metrics', + description: 'Collect AWS DynamoDB metrics', + enabled: true, + }, + ], + package: 'aws', + path: 'dynamodb', + }, + { + type: 'metrics', + dataset: 'aws.ebs', + title: 'AWS EBS metrics', + release: 'beta', + streams: [ + { + input: 'aws/metrics', + vars: [ + { + name: 'period', + type: 'text', + title: 'Period', + multi: false, + required: true, + show_user: true, + default: '5m', + }, + { + name: 'regions', + type: 'text', + title: 'Regions', + multi: true, + required: false, + show_user: true, + }, + { + name: 'latency', + type: 'text', + title: 'Latency', + multi: false, + required: false, + show_user: false, + }, + { + name: 'tags_filter', + type: 'yaml', + title: 'Tags Filter', + multi: false, + required: false, + show_user: false, + default: '# - key: "created-by"\n # value: "foo"\n', + }, + ], + template_path: 'stream.yml.hbs', + title: 'AWS EBS metrics', + description: 'Collect AWS EBS metrics', + enabled: true, + }, + ], + package: 'aws', + path: 'ebs', + }, + { + type: 'logs', + dataset: 'aws.ec2_logs', + title: 'AWS EC2 logs', + release: 'beta', + ingest_pipeline: 'default', + streams: [ + { + input: 's3', + vars: [ + { + name: 'visibility_timeout', + type: 'text', + title: 'Visibility Timeout', + description: + 'The duration that the received messages are hidden from subsequent retrieve requests after being retrieved by a ReceiveMessage request. The maximum is 12 hours.', + multi: false, + required: false, + show_user: false, + }, + { + name: 'api_timeout', + type: 'text', + title: 'API Timeout', + description: + 'The maximum duration of AWS API can take. The maximum is half of the visibility timeout value.', + multi: false, + required: false, + show_user: false, + }, + { + name: 'queue_url', + type: 'text', + title: 'Queue URL', + description: 'URL of the AWS SQS queue that messages will be received from.', + multi: false, + required: true, + show_user: true, + }, + { + name: 'fips_enabled', + type: 'bool', + title: 'Enable S3 FIPS', + description: + 'Enabling this option changes the service name from `s3` to `s3-fips` for connecting to the correct service endpoint.', + multi: false, + required: false, + show_user: false, + default: false, + }, + ], + template_path: 's3.yml.hbs', + title: 'AWS EC2 logs', + description: 'Collect AWS EC2 logs using s3 input', + enabled: true, + }, + ], + package: 'aws', + path: 'ec2_logs', + }, + { + type: 'metrics', + dataset: 'aws.ec2_metrics', + title: 'AWS EC2 metrics', + release: 'beta', + streams: [ + { + input: 'aws/metrics', + vars: [ + { + name: 'period', + type: 'text', + title: 'Period', + multi: false, + required: true, + show_user: true, + default: '5m', + }, + { + name: 'regions', + type: 'text', + title: 'Regions', + multi: true, + required: false, + show_user: true, + }, + { + name: 'latency', + type: 'text', + title: 'Latency', + multi: false, + required: false, + show_user: false, + }, + { + name: 'tags_filter', + type: 'yaml', + title: 'Tags Filter', + multi: false, + required: false, + show_user: false, + default: '# - key: "created-by"\n # value: "foo"\n', + }, + ], + template_path: 'stream.yml.hbs', + title: 'AWS EC2 metrics', + description: 'Collect AWS EC2 metrics', + enabled: true, + }, + ], + package: 'aws', + path: 'ec2_metrics', + }, + { + type: 'logs', + dataset: 'aws.elb_logs', + title: 'AWS ELB logs', + release: 'beta', + ingest_pipeline: 'default', + streams: [ + { + input: 's3', + vars: [ + { + name: 'visibility_timeout', + type: 'text', + title: 'Visibility Timeout', + description: + 'The duration that the received messages are hidden from subsequent retrieve requests after being retrieved by a ReceiveMessage request. The maximum is 12 hours.', + multi: false, + required: false, + show_user: false, + }, + { + name: 'api_timeout', + type: 'text', + title: 'API Timeout', + description: + 'The maximum duration of AWS API can take. The maximum is half of the visibility timeout value.', + multi: false, + required: false, + show_user: false, + }, + { + name: 'queue_url', + type: 'text', + title: 'Queue URL', + description: 'URL of the AWS SQS queue that messages will be received from.', + multi: false, + required: true, + show_user: true, + }, + { + name: 'fips_enabled', + type: 'bool', + title: 'Enable S3 FIPS', + description: + 'Enabling this option changes the service name from `s3` to `s3-fips` for connecting to the correct service endpoint.', + multi: false, + required: false, + show_user: false, + default: false, + }, + ], + template_path: 's3.yml.hbs', + title: 'AWS ELB logs', + description: 'Collect AWS ELB logs using s3 input', + enabled: true, + }, + ], + package: 'aws', + path: 'elb_logs', + }, + { + type: 'metrics', + dataset: 'aws.elb_metrics', + title: 'AWS ELB metrics', + release: 'beta', + streams: [ + { + input: 'aws/metrics', + vars: [ + { + name: 'period', + type: 'text', + title: 'Period', + multi: false, + required: true, + show_user: true, + default: '1m', + }, + { + name: 'regions', + type: 'text', + title: 'Regions', + multi: true, + required: false, + show_user: true, + }, + { + name: 'latency', + type: 'text', + title: 'Latency', + multi: false, + required: false, + show_user: false, + }, + { + name: 'tags_filter', + type: 'yaml', + title: 'Tags Filter', + multi: false, + required: false, + show_user: false, + default: '# - key: "created-by"\n # value: "foo"\n', + }, + ], + template_path: 'stream.yml.hbs', + title: 'AWS ELB metrics', + description: 'Collect AWS ELB metrics', + enabled: true, + }, + ], + package: 'aws', + path: 'elb_metrics', + }, + { + type: 'metrics', + dataset: 'aws.lambda', + title: 'AWS Lambda metrics', + release: 'beta', + streams: [ + { + input: 'aws/metrics', + vars: [ + { + name: 'period', + type: 'text', + title: 'Period', + multi: false, + required: true, + show_user: true, + default: '5m', + }, + { + name: 'regions', + type: 'text', + title: 'Regions', + multi: true, + required: false, + show_user: true, + }, + { + name: 'latency', + type: 'text', + title: 'Latency', + multi: false, + required: false, + show_user: false, + }, + { + name: 'tags_filter', + type: 'yaml', + title: 'Tags Filter', + multi: false, + required: false, + show_user: false, + default: '# - key: "created-by"\n # value: "foo"\n', + }, + ], + template_path: 'stream.yml.hbs', + title: 'AWS Lambda metrics', + description: 'Collect AWS Lambda metrics', + enabled: true, + }, + ], + package: 'aws', + path: 'lambda', + }, + { + type: 'metrics', + dataset: 'aws.natgateway', + title: 'AWS NAT gateway metrics', + release: 'beta', + streams: [ + { + input: 'aws/metrics', + vars: [ + { + name: 'period', + type: 'text', + title: 'Period', + multi: false, + required: true, + show_user: true, + default: '1m', + }, + { + name: 'regions', + type: 'text', + title: 'Regions', + multi: true, + required: false, + show_user: true, + }, + { + name: 'latency', + type: 'text', + title: 'Latency', + multi: false, + required: false, + show_user: false, + }, + ], + template_path: 'stream.yml.hbs', + title: 'AWS NAT gateway metrics', + description: 'Collect AWS NAT gateway metrics', + enabled: true, + }, + ], + package: 'aws', + path: 'natgateway', + }, + { + type: 'metrics', + dataset: 'aws.rds', + title: 'AWS RDS metrics', + release: 'beta', + streams: [ + { + input: 'aws/metrics', + vars: [ + { + name: 'period', + type: 'text', + title: 'Period', + multi: false, + required: true, + show_user: true, + default: '1m', + }, + { + name: 'regions', + type: 'text', + title: 'Regions', + multi: true, + required: false, + show_user: true, + }, + { + name: 'latency', + type: 'text', + title: 'Latency', + multi: false, + required: false, + show_user: false, + }, + { + name: 'tags_filter', + type: 'yaml', + title: 'Tags Filter', + multi: false, + required: false, + show_user: false, + default: '# - key: "created-by"\n # value: "foo"\n', + }, + ], + template_path: 'stream.yml.hbs', + title: 'AWS RDS metrics', + description: 'Collect AWS RDS metrics', + enabled: true, + }, + ], + package: 'aws', + path: 'rds', + }, + { + type: 'metrics', + dataset: 'aws.s3_daily_storage', + title: 'AWS S3 daily storage metrics', + release: 'beta', + streams: [ + { + input: 'aws/metrics', + vars: [ + { + name: 'period', + type: 'text', + title: 'Period', + multi: false, + required: true, + show_user: true, + default: '24h', + }, + { + name: 'regions', + type: 'text', + title: 'Regions', + multi: true, + required: false, + show_user: true, + }, + { + name: 'latency', + type: 'text', + title: 'Latency', + multi: false, + required: false, + show_user: false, + }, + ], + template_path: 'stream.yml.hbs', + title: 'AWS S3 daily storage metrics', + description: 'Collect AWS S3 daily storage metrics', + enabled: true, + }, + ], + package: 'aws', + path: 's3_daily_storage', + }, + { + type: 'metrics', + dataset: 'aws.s3_request', + title: 'AWS S3 request metrics', + release: 'beta', + streams: [ + { + input: 'aws/metrics', + vars: [ + { + name: 'period', + type: 'text', + title: 'Period', + multi: false, + required: true, + show_user: true, + default: '1m', + }, + { + name: 'regions', + type: 'text', + title: 'Regions', + multi: true, + required: false, + show_user: true, + }, + { + name: 'latency', + type: 'text', + title: 'Latency', + multi: false, + required: false, + show_user: false, + }, + ], + template_path: 'stream.yml.hbs', + title: 'AWS S3 request metrics', + description: 'Collect AWS S3 request metrics', + enabled: true, + }, + ], + package: 'aws', + path: 's3_request', + }, + { + type: 'logs', + dataset: 'aws.s3access', + title: 'AWS s3access logs', + release: 'beta', + ingest_pipeline: 'default', + streams: [ + { + input: 's3', + vars: [ + { + name: 'visibility_timeout', + type: 'text', + title: 'Visibility Timeout', + description: + 'The duration that the received messages are hidden from subsequent retrieve requests after being retrieved by a ReceiveMessage request. The maximum is 12 hours.', + multi: false, + required: false, + show_user: false, + }, + { + name: 'api_timeout', + type: 'text', + title: 'API Timeout', + description: + 'The maximum duration of AWS API can take. The maximum is half of the visibility timeout value.', + multi: false, + required: false, + show_user: false, + }, + { + name: 'queue_url', + type: 'text', + title: 'Queue URL', + description: 'URL of the AWS SQS queue that messages will be received from.', + multi: false, + required: true, + show_user: true, + }, + { + name: 'fips_enabled', + type: 'bool', + title: 'Enable S3 FIPS', + description: + 'Enabling this option changes the service name from `s3` to `s3-fips` for connecting to the correct service endpoint.', + multi: false, + required: false, + show_user: false, + default: false, + }, + ], + template_path: 's3.yml.hbs', + title: 'AWS s3access logs', + description: 'Collect AWS s3access logs using s3 input', + enabled: true, + }, + ], + package: 'aws', + path: 's3access', + }, + { + type: 'metrics', + dataset: 'aws.sns', + title: 'AWS SNS metrics', + release: 'beta', + streams: [ + { + input: 'aws/metrics', + vars: [ + { + name: 'period', + type: 'text', + title: 'Period', + multi: false, + required: true, + show_user: true, + default: '5m', + }, + { + name: 'regions', + type: 'text', + title: 'Regions', + multi: true, + required: false, + show_user: true, + }, + { + name: 'latency', + type: 'text', + title: 'Latency', + multi: false, + required: false, + show_user: false, + }, + { + name: 'tags_filter', + type: 'yaml', + title: 'Tags Filter', + multi: false, + required: false, + show_user: false, + default: '# - key: "created-by"\n # value: "foo"\n', + }, + ], + template_path: 'stream.yml.hbs', + title: 'AWS SNS metrics', + description: 'Collect AWS SNS metrics', + enabled: true, + }, + ], + package: 'aws', + path: 'sns', + }, + { + type: 'metrics', + dataset: 'aws.sqs', + title: 'AWS SQS metrics', + release: 'beta', + streams: [ + { + input: 'aws/metrics', + vars: [ + { + name: 'period', + type: 'text', + title: 'Period', + multi: false, + required: true, + show_user: true, + default: '5m', + }, + { + name: 'regions', + type: 'text', + title: 'Regions', + multi: true, + required: false, + show_user: true, + }, + { + name: 'latency', + type: 'text', + title: 'Latency', + multi: false, + required: false, + show_user: false, + }, + ], + template_path: 'stream.yml.hbs', + title: 'AWS SQS metrics', + description: 'Collect AWS SQS metrics', + enabled: true, + }, + ], + package: 'aws', + path: 'sqs', + }, + { + type: 'metrics', + dataset: 'aws.transitgateway', + title: 'AWS Transit Gateway metrics', + release: 'beta', + streams: [ + { + input: 'aws/metrics', + vars: [ + { + name: 'period', + type: 'text', + title: 'Period', + multi: false, + required: true, + show_user: true, + default: '1m', + }, + { + name: 'regions', + type: 'text', + title: 'Regions', + multi: true, + required: false, + show_user: true, + }, + { + name: 'latency', + type: 'text', + title: 'Latency', + multi: false, + required: false, + show_user: false, + }, + ], + template_path: 'stream.yml.hbs', + title: 'AWS Transit Gateway metrics', + description: 'Collect AWS Transit Gateway metrics', + enabled: true, + }, + ], + package: 'aws', + path: 'transitgateway', + }, + { + type: 'metrics', + dataset: 'aws.usage', + title: 'AWS usage metrics', + release: 'beta', + streams: [ + { + input: 'aws/metrics', + vars: [ + { + name: 'period', + type: 'text', + title: 'Period', + multi: false, + required: true, + show_user: true, + default: '1m', + }, + { + name: 'regions', + type: 'text', + title: 'Regions', + multi: true, + required: false, + show_user: true, + }, + { + name: 'latency', + type: 'text', + title: 'Latency', + multi: false, + required: false, + show_user: false, + }, + ], + template_path: 'stream.yml.hbs', + title: 'AWS usage metrics', + description: 'Collect AWS usage metrics', + enabled: true, + }, + ], + package: 'aws', + path: 'usage', + }, + { + type: 'logs', + dataset: 'aws.vpcflow', + title: 'AWS vpcflow logs', + release: 'beta', + ingest_pipeline: 'default', + streams: [ + { + input: 's3', + vars: [ + { + name: 'visibility_timeout', + type: 'text', + title: 'Visibility Timeout', + description: + 'The duration that the received messages are hidden from subsequent retrieve requests after being retrieved by a ReceiveMessage request. The maximum is 12 hours.', + multi: false, + required: false, + show_user: false, + }, + { + name: 'api_timeout', + type: 'text', + title: 'API Timeout', + description: + 'The maximum duration of AWS API can take. The maximum is half of the visibility timeout value.', + multi: false, + required: false, + show_user: false, + }, + { + name: 'queue_url', + type: 'text', + title: 'Queue URL', + description: 'URL of the AWS SQS queue that messages will be received from.', + multi: false, + required: true, + show_user: true, + }, + { + name: 'fips_enabled', + type: 'bool', + title: 'Enable S3 FIPS', + description: + 'Enabling this option changes the service name from `s3` to `s3-fips` for connecting to the correct service endpoint.', + multi: false, + required: false, + show_user: false, + default: false, + }, + ], + template_path: 's3.yml.hbs', + title: 'AWS vpcflow logs', + description: 'Collect AWS vpcflow logs using s3 input', + enabled: true, + }, + ], + package: 'aws', + path: 'vpcflow', + }, + { + type: 'metrics', + dataset: 'aws.vpn', + title: 'AWS VPN metrics', + release: 'beta', + streams: [ + { + input: 'aws/metrics', + vars: [ + { + name: 'period', + type: 'text', + title: 'Period', + multi: false, + required: true, + show_user: true, + default: '1m', + }, + { + name: 'regions', + type: 'text', + title: 'Regions', + multi: true, + required: false, + show_user: true, + }, + { + name: 'latency', + type: 'text', + title: 'Latency', + multi: false, + required: false, + show_user: false, + }, + { + name: 'tags_filter', + type: 'yaml', + title: 'Tags Filter', + multi: false, + required: false, + show_user: false, + default: '# - key: "created-by"\n # value: "foo"\n', + }, + ], + template_path: 'stream.yml.hbs', + title: 'AWS VPN metrics', + description: 'Collect AWS VPN metrics', + enabled: true, + }, + ], + package: 'aws', + path: 'vpn', + }, + ], + owner: { + github: 'elastic/integrations', + }, + vars: [ + { + name: 'shared_credential_file', + type: 'text', + title: 'Shared Credential File', + description: 'Directory of the shared credentials file', + multi: false, + required: false, + show_user: false, + }, + { + name: 'credential_profile_name', + type: 'text', + title: 'Credential Profile Name', + multi: false, + required: false, + show_user: true, + }, + { + name: 'access_key_id', + type: 'text', + title: 'Access Key ID', + multi: false, + required: false, + show_user: false, + }, + { + name: 'secret_access_key', + type: 'text', + title: 'Secret Access Key', + multi: false, + required: false, + show_user: false, + }, + { + name: 'session_token', + type: 'text', + title: 'Session Token', + multi: false, + required: false, + show_user: false, + }, + { + name: 'role_arn', + type: 'text', + title: 'Role ARN', + multi: false, + required: false, + show_user: false, + }, + { + name: 'endpoint', + type: 'text', + title: 'Endpoint', + description: 'URL of the entry point for an AWS web service', + multi: false, + required: false, + show_user: false, + default: 'amazonaws.com', + }, + ], + latestVersion: '0.5.3', + removable: true, + status: 'not_installed', +}; + +export const INVALID_AWS_POLICY = { + name: 'aws-1', + namespace: 'default', + package: { name: 'aws', title: 'AWS', version: '0.5.3' }, + enabled: true, + policy_id: 'some-agent-policy-id', + output_id: 'some-output-id', + inputs: [ + { + type: 'aws/metrics', + policy_template: 'billing', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.billing' }, + vars: { + period: { value: '12h', type: 'text' }, + latency: { type: 'text' }, + 'cost_explorer_config.group_by_dimension_keys': { + value: ['AZ', 'INSTANCE_TYPE', 'SERVICE'], + type: 'text', + }, + 'cost_explorer_config.group_by_tag_keys': { value: ['aws:createdBy'], type: 'text' }, + }, + }, + ], + }, + { + type: 's3', + policy_template: 'cloudtrail', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'logs', dataset: 'aws.cloudtrail' }, + vars: { + visibility_timeout: { type: 'text' }, + api_timeout: { type: 'text' }, + queue_url: { type: 'text' }, + fips_enabled: { value: false, type: 'bool' }, + }, + }, + ], + }, + { + type: 'httpjson', + policy_template: 'cloudtrail', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'logs', dataset: 'aws.cloudtrail' }, + vars: { + url: { value: 'https://server.example.com:8089', type: 'text' }, + username: { type: 'text' }, + password: { type: 'password' }, + ssl: { type: 'yaml' }, + interval: { value: '10s', type: 'text' }, + search: { value: 'search sourcetype=aws:cloudtrail', type: 'text' }, + tags: { value: ['forwarded'], type: 'text' }, + }, + }, + ], + }, + { + type: 's3', + policy_template: 'cloudwatch', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'logs', dataset: 'aws.cloudwatch_logs' }, + vars: { + visibility_timeout: { type: 'text' }, + api_timeout: { type: 'text' }, + queue_url: { type: 'text' }, + fips_enabled: { value: false, type: 'bool' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'cloudwatch', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.cloudwatch_metrics' }, + vars: { + period: { value: '300s', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + metrics: { + value: + '- namespace: AWS/EC2\n resource_type: ec2:instance\n name:\n - CPUUtilization\n - DiskWriteOps\n statistic:\n - Average\n - Maximum\n # dimensions:\n # - name: InstanceId\n # value: i-123456\n # tags:\n # - key: created-by\n # value: foo\n', + type: 'yaml', + }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'dynamodb', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.dynamodb' }, + vars: { + period: { value: '5m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + tags_filter: { value: '# - key: "created-by"\n # value: "foo"\n', type: 'yaml' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'ebs', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.ebs' }, + vars: { + period: { value: '5m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + tags_filter: { value: '# - key: "created-by"\n # value: "foo"\n', type: 'yaml' }, + }, + }, + ], + }, + { + type: 's3', + policy_template: 'ec2', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'logs', dataset: 'aws.ec2_logs' }, + vars: { + visibility_timeout: { type: 'text' }, + api_timeout: { type: 'text' }, + queue_url: { type: 'text' }, + fips_enabled: { value: false, type: 'bool' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'ec2', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.ec2_metrics' }, + vars: { + period: { value: '5m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + tags_filter: { value: '# - key: "created-by"\n # value: "foo"\n', type: 'yaml' }, + }, + }, + ], + }, + { + type: 's3', + policy_template: 'elb', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'logs', dataset: 'aws.elb_logs' }, + vars: { + visibility_timeout: { type: 'text' }, + api_timeout: { type: 'text' }, + queue_url: { type: 'text' }, + fips_enabled: { value: false, type: 'bool' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'elb', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.elb_metrics' }, + vars: { + period: { value: '1m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + tags_filter: { value: '# - key: "created-by"\n # value: "foo"\n', type: 'yaml' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'lambda', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.lambda' }, + vars: { + period: { value: '5m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + tags_filter: { value: '# - key: "created-by"\n # value: "foo"\n', type: 'yaml' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'natgateway', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.natgateway' }, + vars: { + period: { value: '1m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'rds', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.rds' }, + vars: { + period: { value: '1m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + tags_filter: { value: '# - key: "created-by"\n # value: "foo"\n', type: 'yaml' }, + }, + }, + ], + }, + { + type: 's3', + policy_template: 's3', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'logs', dataset: 'aws.s3access' }, + vars: { + visibility_timeout: { type: 'text' }, + api_timeout: { type: 'text' }, + queue_url: { type: 'text' }, + fips_enabled: { value: false, type: 'bool' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 's3', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.s3_daily_storage' }, + vars: { + period: { value: '24h', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + }, + }, + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.s3_request' }, + vars: { + period: { value: '1m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'sns', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.sns' }, + vars: { + period: { value: '5m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + tags_filter: { value: '# - key: "created-by"\n # value: "foo"\n', type: 'yaml' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'sqs', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.sqs' }, + vars: { + period: { value: '5m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'transitgateway', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.transitgateway' }, + vars: { + period: { value: '1m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'usage', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.usage' }, + vars: { + period: { value: '1m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + }, + }, + ], + }, + { + type: 's3', + policy_template: 'vpcflow', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'logs', dataset: 'aws.vpcflow' }, + vars: { + visibility_timeout: { type: 'text' }, + api_timeout: { type: 'text' }, + queue_url: { type: 'text' }, + fips_enabled: { value: false, type: 'bool' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'vpn', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.vpn' }, + vars: { + period: { value: '1m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + tags_filter: { value: '# - key: "created-by"\n # value: "foo"\n', type: 'yaml' }, + }, + }, + ], + }, + ], + vars: { + shared_credential_file: { type: 'text' }, + credential_profile_name: { type: 'text' }, + access_key_id: { type: 'text' }, + secret_access_key: { type: 'text' }, + session_token: { type: 'text' }, + role_arn: { type: 'text' }, + endpoint: { value: 'amazonaws.com', type: 'text' }, + }, +}; + +export const VALID_AWS_POLICY = { + name: 'aws-1', + namespace: 'default', + package: { name: 'aws', title: 'AWS', version: '0.5.3' }, + enabled: true, + policy_id: 'some-agent-policy-id', + output_id: 'some-output-id', + inputs: [ + { + type: 'aws/metrics', + policy_template: 'billing', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.billing' }, + vars: { + period: { value: '12h', type: 'text' }, + latency: { type: 'text' }, + 'cost_explorer_config.group_by_dimension_keys': { + value: ['AZ', 'INSTANCE_TYPE', 'SERVICE'], + type: 'text', + }, + 'cost_explorer_config.group_by_tag_keys': { value: ['aws:createdBy'], type: 'text' }, + }, + }, + ], + }, + { + type: 's3', + policy_template: 'cloudtrail', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'logs', dataset: 'aws.cloudtrail' }, + vars: { + visibility_timeout: { type: 'text' }, + api_timeout: { type: 'text' }, + queue_url: { type: 'text', value: 'http://localhost' }, + fips_enabled: { value: false, type: 'bool' }, + }, + }, + ], + }, + { + type: 'httpjson', + policy_template: 'cloudtrail', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'logs', dataset: 'aws.cloudtrail' }, + vars: { + url: { value: 'https://server.example.com:8089', type: 'text' }, + username: { type: 'text' }, + password: { type: 'password' }, + ssl: { type: 'yaml' }, + interval: { value: '10s', type: 'text' }, + search: { value: 'search sourcetype=aws:cloudtrail', type: 'text' }, + tags: { value: ['forwarded'], type: 'text' }, + }, + }, + ], + }, + { + type: 's3', + policy_template: 'cloudwatch', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'logs', dataset: 'aws.cloudwatch_logs' }, + vars: { + visibility_timeout: { type: 'text' }, + api_timeout: { type: 'text' }, + queue_url: { type: 'text', value: 'http://localhost' }, + fips_enabled: { value: false, type: 'bool' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'cloudwatch', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.cloudwatch_metrics' }, + vars: { + period: { value: '300s', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + metrics: { + value: + '- namespace: AWS/EC2\n resource_type: ec2:instance\n name:\n - CPUUtilization\n - DiskWriteOps\n statistic:\n - Average\n - Maximum\n # dimensions:\n # - name: InstanceId\n # value: i-123456\n # tags:\n # - key: created-by\n # value: foo\n', + type: 'yaml', + }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'dynamodb', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.dynamodb' }, + vars: { + period: { value: '5m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + tags_filter: { value: '# - key: "created-by"\n # value: "foo"\n', type: 'yaml' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'ebs', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.ebs' }, + vars: { + period: { value: '5m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + tags_filter: { value: '# - key: "created-by"\n # value: "foo"\n', type: 'yaml' }, + }, + }, + ], + }, + { + type: 's3', + policy_template: 'ec2', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'logs', dataset: 'aws.ec2_logs' }, + vars: { + visibility_timeout: { type: 'text' }, + api_timeout: { type: 'text' }, + queue_url: { type: 'text', value: 'http://localhost' }, + fips_enabled: { value: false, type: 'bool' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'ec2', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.ec2_metrics' }, + vars: { + period: { value: '5m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + tags_filter: { value: '# - key: "created-by"\n # value: "foo"\n', type: 'yaml' }, + }, + }, + ], + }, + { + type: 's3', + policy_template: 'elb', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'logs', dataset: 'aws.elb_logs' }, + vars: { + visibility_timeout: { type: 'text' }, + api_timeout: { type: 'text' }, + queue_url: { type: 'text', value: 'http://localhost' }, + fips_enabled: { value: false, type: 'bool' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'elb', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.elb_metrics' }, + vars: { + period: { value: '1m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + tags_filter: { value: '# - key: "created-by"\n # value: "foo"\n', type: 'yaml' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'lambda', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.lambda' }, + vars: { + period: { value: '5m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + tags_filter: { value: '# - key: "created-by"\n # value: "foo"\n', type: 'yaml' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'natgateway', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.natgateway' }, + vars: { + period: { value: '1m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'rds', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.rds' }, + vars: { + period: { value: '1m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + tags_filter: { value: '# - key: "created-by"\n # value: "foo"\n', type: 'yaml' }, + }, + }, + ], + }, + { + type: 's3', + policy_template: 's3', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'logs', dataset: 'aws.s3access' }, + vars: { + visibility_timeout: { type: 'text' }, + api_timeout: { type: 'text' }, + queue_url: { type: 'text', value: 'http://localhost' }, + fips_enabled: { value: false, type: 'bool' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 's3', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.s3_daily_storage' }, + vars: { + period: { value: '24h', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + }, + }, + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.s3_request' }, + vars: { + period: { value: '1m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'sns', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.sns' }, + vars: { + period: { value: '5m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + tags_filter: { value: '# - key: "created-by"\n # value: "foo"\n', type: 'yaml' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'sqs', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.sqs' }, + vars: { + period: { value: '5m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'transitgateway', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.transitgateway' }, + vars: { + period: { value: '1m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'usage', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.usage' }, + vars: { + period: { value: '1m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + }, + }, + ], + }, + { + type: 's3', + policy_template: 'vpcflow', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'logs', dataset: 'aws.vpcflow' }, + vars: { + visibility_timeout: { type: 'text' }, + api_timeout: { type: 'text' }, + queue_url: { type: 'text', value: 'http://localhost' }, + fips_enabled: { value: false, type: 'bool' }, + }, + }, + ], + }, + { + type: 'aws/metrics', + policy_template: 'vpn', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.vpn' }, + vars: { + period: { value: '1m', type: 'text' }, + regions: { value: [], type: 'text' }, + latency: { type: 'text' }, + tags_filter: { value: '# - key: "created-by"\n # value: "foo"\n', type: 'yaml' }, + }, + }, + ], + }, + ], + vars: { + shared_credential_file: { type: 'text' }, + credential_profile_name: { type: 'text' }, + access_key_id: { type: 'text' }, + secret_access_key: { type: 'text' }, + session_token: { type: 'text' }, + role_arn: { type: 'text' }, + endpoint: { value: 'amazonaws.com', type: 'text' }, + }, +}; diff --git a/x-pack/plugins/fleet/common/services/index.ts b/x-pack/plugins/fleet/common/services/index.ts index 1fea5033e645..86361ae16339 100644 --- a/x-pack/plugins/fleet/common/services/index.ts +++ b/x-pack/plugins/fleet/common/services/index.ts @@ -7,7 +7,11 @@ export * from './routes'; export * as AgentStatusKueryHelper from './agent_status'; -export { packageToPackagePolicyInputs, packageToPackagePolicy } from './package_to_package_policy'; +export { + packageToPackagePolicyInputs, + packageToPackagePolicy, + getStreamsForInputType, +} from './package_to_package_policy'; export { storedPackagePoliciesToAgentInputs } from './package_policies_to_agent_inputs'; export { fullAgentPolicyToYaml } from './full_agent_policy_to_yaml'; export { isPackageLimited, doesAgentPolicyAlreadyIncludePackage } from './limited_package'; @@ -17,3 +21,12 @@ export { isDiffPathProtocol } from './is_diff_path_protocol'; export { LicenseService } from './license'; export { isAgentUpgradeable } from './is_agent_upgradeable'; export { doesPackageHaveIntegrations } from './packages_with_integrations'; +export { + PackagePolicyValidationResults, + PackagePolicyConfigValidationResults, + PackagePolicyInputValidationResults, + validatePackagePolicy, + validatePackagePolicyConfig, + validationHasErrors, + countValidationErrors, +} from './validate_package_policy'; diff --git a/x-pack/plugins/fleet/common/services/limited_package.ts b/x-pack/plugins/fleet/common/services/limited_package.ts index e247b61869de..601f680c8bf0 100644 --- a/x-pack/plugins/fleet/common/services/limited_package.ts +++ b/x-pack/plugins/fleet/common/services/limited_package.ts @@ -7,9 +7,10 @@ import type { PackageInfo, AgentPolicy, PackagePolicy } from '../types'; -// Assume packages only ever include 1 config template for now export const isPackageLimited = (packageInfo: PackageInfo): boolean => { - return packageInfo.policy_templates?.[0]?.multiple === false; + return (packageInfo.policy_templates || []).some( + (policyTemplate) => policyTemplate.multiple === false + ); }; export const doesAgentPolicyAlreadyIncludePackage = ( diff --git a/x-pack/plugins/fleet/common/services/package_to_package_policy.test.ts b/x-pack/plugins/fleet/common/services/package_to_package_policy.test.ts index 65b853ed5b38..0e5cab08edfb 100644 --- a/x-pack/plugins/fleet/common/services/package_to_package_policy.test.ts +++ b/x-pack/plugins/fleet/common/services/package_to_package_policy.test.ts @@ -8,6 +8,7 @@ import type { PackageInfo } from '../types'; import { packageToPackagePolicy, packageToPackagePolicyInputs } from './package_to_package_policy'; +import { AWS_PACKAGE } from './fixtures/aws_package'; describe('Fleet - packageToPackagePolicy', () => { const mockPackage: PackageInfo = { @@ -59,7 +60,7 @@ describe('Fleet - packageToPackagePolicy', () => { expect( packageToPackagePolicyInputs(({ ...mockPackage, - policy_templates: [{ inputs: [] }], + policy_templates: [{ name: 'test_template', inputs: [] }], } as unknown) as PackageInfo) ).toEqual([]); }); @@ -68,17 +69,17 @@ describe('Fleet - packageToPackagePolicy', () => { expect( packageToPackagePolicyInputs(({ ...mockPackage, - policy_templates: [{ inputs: [{ type: 'foo' }] }], + policy_templates: [{ name: 'test_template', inputs: [{ type: 'foo' }] }], } as unknown) as PackageInfo) - ).toEqual([{ type: 'foo', enabled: true, streams: [] }]); + ).toEqual([{ type: 'foo', enabled: true, policy_template: 'test_template', streams: [] }]); expect( packageToPackagePolicyInputs(({ ...mockPackage, - policy_templates: [{ inputs: [{ type: 'foo' }, { type: 'bar' }] }], + policy_templates: [{ name: 'test_template', inputs: [{ type: 'foo' }, { type: 'bar' }] }], } as unknown) as PackageInfo) ).toEqual([ - { type: 'foo', enabled: true, streams: [] }, - { type: 'bar', enabled: true, streams: [] }, + { type: 'foo', enabled: true, policy_template: 'test_template', streams: [] }, + { type: 'bar', enabled: true, policy_template: 'test_template', streams: [] }, ]); }); @@ -91,24 +92,34 @@ describe('Fleet - packageToPackagePolicy', () => { { type: 'logs', dataset: 'bar', streams: [{ input: 'bar' }] }, { type: 'logs', dataset: 'bar2', streams: [{ input: 'bar' }] }, ], - policy_templates: [ - { - inputs: [{ type: 'foo' }, { type: 'bar' }], - }, - ], + policy_templates: [{ name: 'test_template', inputs: [{ type: 'foo' }, { type: 'bar' }] }], } as unknown) as PackageInfo) ).toEqual([ { type: 'foo', + policy_template: 'test_template', enabled: true, - streams: [{ enabled: true, data_stream: { dataset: 'foo', type: 'logs' } }], + streams: [ + { + enabled: true, + data_stream: { dataset: 'foo', type: 'logs' }, + }, + ], }, { type: 'bar', + policy_template: 'test_template', + enabled: true, streams: [ - { enabled: true, data_stream: { dataset: 'bar', type: 'logs' } }, - { enabled: true, data_stream: { dataset: 'bar2', type: 'logs' } }, + { + enabled: true, + data_stream: { dataset: 'bar', type: 'logs' }, + }, + { + enabled: true, + data_stream: { dataset: 'bar2', type: 'logs' }, + }, ], }, ]); @@ -145,15 +156,12 @@ describe('Fleet - packageToPackagePolicy', () => { ], }, ], - policy_templates: [ - { - inputs: [{ type: 'foo' }, { type: 'bar' }], - }, - ], + policy_templates: [{ name: 'test_template', inputs: [{ type: 'foo' }, { type: 'bar' }] }], } as unknown) as PackageInfo) ).toEqual([ { type: 'foo', + policy_template: 'test_template', enabled: true, streams: [ { @@ -165,6 +173,7 @@ describe('Fleet - packageToPackagePolicy', () => { }, { type: 'bar', + policy_template: 'test_template', enabled: true, streams: [ { @@ -236,6 +245,7 @@ describe('Fleet - packageToPackagePolicy', () => { ], policy_templates: [ { + name: 'test_template', inputs: [ { type: 'foo', @@ -262,6 +272,8 @@ describe('Fleet - packageToPackagePolicy', () => { ).toEqual([ { type: 'foo', + policy_template: 'test_template', + enabled: true, vars: { 'foo-input-var-name': { value: 'foo-input-var-value' }, @@ -280,6 +292,8 @@ describe('Fleet - packageToPackagePolicy', () => { }, { type: 'bar', + policy_template: 'test_template', + enabled: true, vars: { 'bar-input-var-name': { value: ['value1', 'value2'] }, @@ -304,6 +318,8 @@ describe('Fleet - packageToPackagePolicy', () => { }, { type: 'with-disabled-streams', + policy_template: 'test_template', + enabled: false, streams: [ { @@ -339,6 +355,7 @@ describe('Fleet - packageToPackagePolicy', () => { }, }); }); + it('returns package policy with custom name', () => { expect(packageToPackagePolicy(mockPackage, '1', '2', 'default', 'pkgPolicy-1')).toEqual({ policy_id: '1', @@ -354,6 +371,7 @@ describe('Fleet - packageToPackagePolicy', () => { }, }); }); + it('returns package policy with namespace and description', () => { expect( packageToPackagePolicy( @@ -379,6 +397,7 @@ describe('Fleet - packageToPackagePolicy', () => { }, }); }); + it('returns package policy with inputs', () => { const mockPackageWithPolicyTemplates = ({ ...mockPackage, @@ -401,5 +420,17 @@ describe('Fleet - packageToPackagePolicy', () => { }, }); }); + + it('returns package policy with multiple policy templates (aka has integrations', () => { + expect( + packageToPackagePolicy( + (AWS_PACKAGE as unknown) as PackageInfo, + 'some-agent-policy-id', + 'some-output-id', + 'default', + 'aws-1' + ) + ).toMatchSnapshot(); + }); }); }); diff --git a/x-pack/plugins/fleet/common/services/package_to_package_policy.ts b/x-pack/plugins/fleet/common/services/package_to_package_policy.ts index 8f79e633eed0..0d40adb4bf7d 100644 --- a/x-pack/plugins/fleet/common/services/package_to_package_policy.ts +++ b/x-pack/plugins/fleet/common/services/package_to_package_policy.ts @@ -7,8 +7,8 @@ import type { PackageInfo, - RegistryPolicyTemplate, RegistryVarsEntry, + RegistryInput, RegistryStream, PackagePolicyConfigRecord, NewPackagePolicyInput, @@ -17,13 +17,20 @@ import type { PackagePolicyConfigRecordEntry, } from '../types'; -const getStreamsForInputType = ( +import { doesPackageHaveIntegrations } from './'; + +export const getStreamsForInputType = ( inputType: string, - packageInfo: PackageInfo + packageInfo: PackageInfo, + dataStreamPaths: string[] = [] ): Array => { const streams: Array = []; + const dataStreams = packageInfo.data_streams || []; + const dataStreamsToSearch = dataStreamPaths.length + ? dataStreams.filter((dataStream) => dataStreamPaths.includes(dataStream.path)) + : dataStreams; - (packageInfo.data_streams || []).forEach((dataStream) => { + dataStreamsToSearch.forEach((dataStream) => { (dataStream.streams || []).forEach((stream) => { if (stream.input === inputType) { streams.push({ @@ -59,48 +66,87 @@ const varsReducer = ( * This service creates a package policy inputs definition from defaults provided in package info */ export const packageToPackagePolicyInputs = ( - packageInfo: PackageInfo -): NewPackagePolicy['inputs'] => { - const inputs: NewPackagePolicy['inputs'] = []; - - // Assume package will only ever ship one package policy template for now - const packagePolicyTemplate: RegistryPolicyTemplate | null = - packageInfo.policy_templates && packageInfo.policy_templates[0] - ? packageInfo.policy_templates[0] - : null; - - // Create package policy input property - if (packagePolicyTemplate?.inputs?.length) { - // Map each package package policy input to agent policy package policy input - packagePolicyTemplate.inputs.forEach((packageInput) => { - // Map each package input stream into package policy input stream - const streams: NewPackagePolicyInputStream[] = getStreamsForInputType( - packageInput.type, - packageInfo - ).map((packageStream) => { - const stream: NewPackagePolicyInputStream = { - enabled: packageStream.enabled === false ? false : true, - data_stream: packageStream.data_stream, - }; - if (packageStream.vars && packageStream.vars.length) { - stream.vars = packageStream.vars.reduce(varsReducer, {}); - } - return stream; - }); - - const input: NewPackagePolicyInput = { - type: packageInput.type, - enabled: streams.length ? !!streams.find((stream) => stream.enabled) : true, - streams, + packageInfo: PackageInfo, + integrationToEnable?: string +): NewPackagePolicyInput[] => { + const hasIntegrations = doesPackageHaveIntegrations(packageInfo); + const inputs: NewPackagePolicyInput[] = []; + const packageInputsByPolicyTemplateAndType: { + [key: string]: RegistryInput & { data_streams?: string[]; policy_template: string }; + } = {}; + + packageInfo.policy_templates?.forEach((packagePolicyTemplate) => { + packagePolicyTemplate.inputs?.forEach((packageInput) => { + const inputKey = `${packagePolicyTemplate.name}-${packageInput.type}`; + const input = { + ...packageInput, + ...(packagePolicyTemplate.data_streams + ? { data_streams: packagePolicyTemplate.data_streams } + : {}), + policy_template: packagePolicyTemplate.name, }; + packageInputsByPolicyTemplateAndType[inputKey] = input; + }); + }); - if (packageInput.vars && packageInput.vars.length) { - input.vars = packageInput.vars.reduce(varsReducer, {}); - } + Object.values(packageInputsByPolicyTemplateAndType).forEach((packageInput) => { + const streamsForInput: NewPackagePolicyInputStream[] = []; + let varsForInput: PackagePolicyConfigRecord = {}; - inputs.push(input); + // Map each package input stream into package policy input stream + const streams = getStreamsForInputType( + packageInput.type, + packageInfo, + packageInput.data_streams + ).map((packageStream) => { + const stream: NewPackagePolicyInputStream = { + enabled: packageStream.enabled === false ? false : true, + data_stream: packageStream.data_stream, + }; + if (packageStream.vars && packageStream.vars.length) { + stream.vars = packageStream.vars.reduce(varsReducer, {}); + } + return stream; }); - } + + // If non-integration package, collect input-level vars, otherwise skip them, + // we do not support input-level vars for packages with integrations yet) + if (packageInput.vars?.length && !hasIntegrations) { + varsForInput = packageInput.vars.reduce(varsReducer, {}); + } + + streamsForInput.push(...streams); + + // Check if we should enable this input by the streams below it + // Enable it if at least one of its streams is enabled + let enableInput = streamsForInput.length + ? !!streamsForInput.find((stream) => stream.enabled) + : true; + + // If we are wanting to enabling this input, check if we only want + // to enable specific integrations (aka `policy_template`s) + if ( + enableInput && + hasIntegrations && + integrationToEnable && + integrationToEnable !== packageInput.policy_template + ) { + enableInput = false; + } + + const input: NewPackagePolicyInput = { + type: packageInput.type, + policy_template: packageInput.policy_template, + enabled: enableInput, + streams: streamsForInput, + }; + + if (Object.keys(varsForInput).length) { + input.vars = varsForInput; + } + + inputs.push(input); + }); return inputs; }; @@ -119,7 +165,8 @@ export const packageToPackagePolicy = ( outputId: string, namespace: string = '', packagePolicyName?: string, - description?: string + description?: string, + integrationToEnable?: string ): NewPackagePolicy => { const packagePolicy: NewPackagePolicy = { name: packagePolicyName || `${packageInfo.name}-1`, @@ -133,7 +180,8 @@ export const packageToPackagePolicy = ( enabled: true, policy_id: agentPolicyId, output_id: outputId, - inputs: packageToPackagePolicyInputs(packageInfo), + inputs: packageToPackagePolicyInputs(packageInfo, integrationToEnable), + vars: undefined, }; if (packageInfo.vars?.length) { diff --git a/x-pack/plugins/fleet/common/services/validate_package_policy.test.ts b/x-pack/plugins/fleet/common/services/validate_package_policy.test.ts new file mode 100644 index 000000000000..95dbf156040a --- /dev/null +++ b/x-pack/plugins/fleet/common/services/validate_package_policy.test.ts @@ -0,0 +1,635 @@ +/* + * 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 { installationStatuses } from '../constants'; +import type { PackageInfo, NewPackagePolicy, RegistryPolicyTemplate } from '../types'; + +import { validatePackagePolicy, validationHasErrors } from './validate_package_policy'; +import { AWS_PACKAGE, INVALID_AWS_POLICY, VALID_AWS_POLICY } from './fixtures/aws_package'; + +describe('Fleet - validatePackagePolicy()', () => { + describe('works for packages with single policy template (aka no integrations)', () => { + const mockPackage = ({ + name: 'mock-package', + title: 'Mock package', + version: '0.0.0', + description: 'description', + type: 'mock', + categories: [], + requirement: { kibana: { versions: '' }, elasticsearch: { versions: '' } }, + format_version: '', + download: '', + path: '', + assets: { + kibana: { + dashboard: [], + visualization: [], + search: [], + 'index-pattern': [], + }, + }, + status: installationStatuses.NotInstalled, + data_streams: [ + { + dataset: 'foo', + streams: [ + { + input: 'foo', + title: 'Foo', + vars: [{ name: 'var-name', type: 'yaml' }], + }, + ], + }, + { + dataset: 'bar', + streams: [ + { + input: 'bar', + title: 'Bar', + vars: [{ name: 'var-name', type: 'yaml', required: true }], + }, + { + input: 'with-no-stream-vars', + title: 'Bar stream no vars', + enabled: true, + }, + ], + }, + { + dataset: 'bar2', + streams: [ + { + input: 'bar', + title: 'Bar 2', + vars: [{ default: 'bar2-var-value', name: 'var-name', type: 'text' }], + }, + ], + }, + { + dataset: 'disabled', + streams: [ + { + input: 'with-disabled-streams', + title: 'Disabled', + enabled: false, + vars: [{ multi: true, required: true, name: 'var-name', type: 'text' }], + }, + ], + }, + { + dataset: 'disabled2', + streams: [ + { + input: 'with-disabled-streams', + title: 'Disabled 2', + enabled: false, + }, + ], + }, + ], + policy_templates: [ + { + name: 'pkgPolicy1', + title: 'Package policy 1', + description: 'test package policy', + inputs: [ + { + type: 'foo', + title: 'Foo', + vars: [ + { default: 'foo-input-var-value', name: 'foo-input-var-name', type: 'text' }, + { + default: 'foo-input2-var-value', + name: 'foo-input2-var-name', + required: true, + type: 'text', + }, + { name: 'foo-input3-var-name', type: 'text', required: true, multi: true }, + ], + }, + { + type: 'bar', + title: 'Bar', + vars: [ + { + default: ['value1', 'value2'], + name: 'bar-input-var-name', + type: 'text', + multi: true, + }, + { name: 'bar-input2-var-name', required: true, type: 'text' }, + ], + }, + { + type: 'with-no-config-or-streams', + title: 'With no config or streams', + }, + { + type: 'with-disabled-streams', + title: 'With disabled streams', + }, + { + type: 'with-no-stream-vars', + enabled: true, + vars: [{ required: true, name: 'var-name', type: 'text' }], + }, + ], + }, + ], + } as unknown) as PackageInfo; + + const validPackagePolicy: NewPackagePolicy = { + name: 'pkgPolicy1-1', + namespace: 'default', + policy_id: 'test-policy', + enabled: true, + output_id: 'test-output', + inputs: [ + { + type: 'foo', + policy_template: 'pkgPolicy1', + enabled: true, + vars: { + 'foo-input-var-name': { value: 'foo-input-var-value', type: 'text' }, + 'foo-input2-var-name': { value: 'foo-input2-var-value', type: 'text' }, + 'foo-input3-var-name': { value: ['test'], type: 'text' }, + }, + streams: [ + { + data_stream: { dataset: 'foo', type: 'logs' }, + enabled: true, + vars: { 'var-name': { value: 'test_yaml: value', type: 'yaml' } }, + }, + ], + }, + { + type: 'bar', + policy_template: 'pkgPolicy1', + enabled: true, + vars: { + 'bar-input-var-name': { value: ['value1', 'value2'], type: 'text' }, + 'bar-input2-var-name': { value: 'test', type: 'text' }, + }, + streams: [ + { + data_stream: { dataset: 'bar', type: 'logs' }, + enabled: true, + vars: { 'var-name': { value: 'test_yaml: value', type: 'yaml' } }, + }, + { + data_stream: { dataset: 'bar2', type: 'logs' }, + enabled: true, + vars: { 'var-name': { value: undefined, type: 'text' } }, + }, + ], + }, + { + type: 'with-no-config-or-streams', + policy_template: 'pkgPolicy1', + enabled: true, + streams: [], + }, + { + type: 'with-disabled-streams', + policy_template: 'pkgPolicy1', + enabled: true, + streams: [ + { + data_stream: { dataset: 'disabled', type: 'logs' }, + enabled: false, + vars: { 'var-name': { value: undefined, type: 'text' } }, + }, + { + data_stream: { dataset: 'disabled2', type: 'logs' }, + enabled: false, + }, + ], + }, + { + type: 'with-no-stream-vars', + policy_template: 'pkgPolicy1', + enabled: true, + vars: { + 'var-name': { value: 'test', type: 'text' }, + }, + streams: [ + { + data_stream: { dataset: 'with-no-stream-vars-bar', type: 'logs' }, + enabled: true, + }, + ], + }, + ], + }; + + const invalidPackagePolicy: NewPackagePolicy = { + ...validPackagePolicy, + name: '', + inputs: [ + { + type: 'foo', + policy_template: 'pkgPolicy1', + enabled: true, + vars: { + 'foo-input-var-name': { value: undefined, type: 'text' }, + 'foo-input2-var-name': { value: '', type: 'text' }, + 'foo-input3-var-name': { value: [], type: 'text' }, + }, + streams: [ + { + data_stream: { dataset: 'foo', type: 'logs' }, + enabled: true, + vars: { 'var-name': { value: 'invalidyaml: test\n foo bar:', type: 'yaml' } }, + }, + ], + }, + { + type: 'bar', + policy_template: 'pkgPolicy1', + enabled: true, + vars: { + 'bar-input-var-name': { value: 'invalid value for multi', type: 'text' }, + 'bar-input2-var-name': { value: undefined, type: 'text' }, + }, + streams: [ + { + data_stream: { dataset: 'bar', type: 'logs' }, + enabled: true, + vars: { 'var-name': { value: ' \n\n', type: 'yaml' } }, + }, + { + data_stream: { dataset: 'bar2', type: 'logs' }, + enabled: true, + vars: { 'var-name': { value: undefined, type: 'text' } }, + }, + ], + }, + { + type: 'with-no-config-or-streams', + policy_template: 'pkgPolicy1', + enabled: true, + streams: [], + }, + { + type: 'with-disabled-streams', + policy_template: 'pkgPolicy1', + enabled: true, + streams: [ + { + data_stream: { dataset: 'disabled', type: 'logs' }, + enabled: false, + vars: { + 'var-name': { + value: 'invalid value but not checked due to not enabled', + type: 'text', + }, + }, + }, + { + data_stream: { dataset: 'disabled2', type: 'logs' }, + enabled: false, + }, + ], + }, + { + type: 'with-no-stream-vars', + policy_template: 'pkgPolicy1', + enabled: true, + vars: { + 'var-name': { value: undefined, type: 'text' }, + }, + streams: [ + { + data_stream: { dataset: 'with-no-stream-vars-bar', type: 'logs' }, + enabled: true, + }, + ], + }, + ], + }; + + const noErrorsValidationResults = { + name: null, + description: null, + namespace: null, + inputs: { + foo: { + vars: { + 'foo-input-var-name': null, + 'foo-input2-var-name': null, + 'foo-input3-var-name': null, + }, + streams: { foo: { vars: { 'var-name': null } } }, + }, + bar: { + vars: { 'bar-input-var-name': null, 'bar-input2-var-name': null }, + streams: { + bar: { vars: { 'var-name': null } }, + bar2: { vars: { 'var-name': null } }, + }, + }, + 'with-disabled-streams': { + streams: { + disabled: { + vars: { 'var-name': null }, + }, + disabled2: {}, + }, + }, + 'with-no-stream-vars': { + streams: { + 'with-no-stream-vars-bar': {}, + }, + vars: { 'var-name': null }, + }, + }, + }; + + it('returns no errors for valid package policy', () => { + expect(validatePackagePolicy(validPackagePolicy, mockPackage)).toEqual( + noErrorsValidationResults + ); + }); + + it('returns errors for invalid package policy', () => { + expect(validatePackagePolicy(invalidPackagePolicy, mockPackage)).toEqual({ + name: ['Name is required'], + description: null, + namespace: null, + inputs: { + foo: { + vars: { + 'foo-input-var-name': null, + 'foo-input2-var-name': ['foo-input2-var-name is required'], + 'foo-input3-var-name': ['foo-input3-var-name is required'], + }, + streams: { foo: { vars: { 'var-name': ['Invalid YAML format'] } } }, + }, + bar: { + vars: { + 'bar-input-var-name': ['Invalid format'], + 'bar-input2-var-name': ['bar-input2-var-name is required'], + }, + streams: { + bar: { vars: { 'var-name': ['var-name is required'] } }, + bar2: { vars: { 'var-name': null } }, + }, + }, + 'with-disabled-streams': { + streams: { + disabled: { vars: { 'var-name': null } }, + disabled2: {}, + }, + }, + 'with-no-stream-vars': { + vars: { + 'var-name': ['var-name is required'], + }, + streams: { 'with-no-stream-vars-bar': {} }, + }, + }, + }); + }); + + it('returns no errors for disabled inputs', () => { + const disabledInputs = invalidPackagePolicy.inputs.map((input) => ({ + ...input, + enabled: false, + })); + expect( + validatePackagePolicy({ ...validPackagePolicy, inputs: disabledInputs }, mockPackage) + ).toEqual(noErrorsValidationResults); + }); + + it('returns only package policy and input-level errors for disabled streams', () => { + const inputsWithDisabledStreams = invalidPackagePolicy.inputs.map((input) => + input.streams + ? { + ...input, + streams: input.streams.map((stream) => ({ ...stream, enabled: false })), + } + : input + ); + expect( + validatePackagePolicy( + { ...invalidPackagePolicy, inputs: inputsWithDisabledStreams }, + mockPackage + ) + ).toEqual({ + name: ['Name is required'], + description: null, + namespace: null, + inputs: { + foo: { + vars: { + 'foo-input-var-name': null, + 'foo-input2-var-name': ['foo-input2-var-name is required'], + 'foo-input3-var-name': ['foo-input3-var-name is required'], + }, + streams: { foo: { vars: { 'var-name': null } } }, + }, + bar: { + vars: { + 'bar-input-var-name': ['Invalid format'], + 'bar-input2-var-name': ['bar-input2-var-name is required'], + }, + streams: { + bar: { vars: { 'var-name': null } }, + bar2: { vars: { 'var-name': null } }, + }, + }, + 'with-disabled-streams': { + streams: { + disabled: { + vars: { 'var-name': null }, + }, + disabled2: {}, + }, + }, + 'with-no-stream-vars': { + vars: { + 'var-name': ['var-name is required'], + }, + streams: { 'with-no-stream-vars-bar': {} }, + }, + }, + }); + }); + + it('returns no errors for packages with no package policies', () => { + expect( + validatePackagePolicy(validPackagePolicy, { + ...mockPackage, + policy_templates: undefined, + }) + ).toEqual({ + name: null, + description: null, + namespace: null, + inputs: null, + }); + expect( + validatePackagePolicy(validPackagePolicy, { + ...mockPackage, + policy_templates: [], + }) + ).toEqual({ + name: null, + description: null, + namespace: null, + inputs: null, + }); + }); + + it('returns no errors for packages with no inputs', () => { + expect( + validatePackagePolicy(validPackagePolicy, { + ...mockPackage, + policy_templates: [{} as RegistryPolicyTemplate], + }) + ).toEqual({ + name: null, + description: null, + namespace: null, + inputs: null, + }); + expect( + validatePackagePolicy(validPackagePolicy, { + ...mockPackage, + policy_templates: [({ inputs: [] } as unknown) as RegistryPolicyTemplate], + }) + ).toEqual({ + name: null, + description: null, + namespace: null, + inputs: null, + }); + }); + }); + + describe('works for packages with multiple policy templates (aka integrations)', () => { + it('returns errors for invalid package policy', () => { + expect( + validatePackagePolicy( + INVALID_AWS_POLICY as NewPackagePolicy, + (AWS_PACKAGE as unknown) as PackageInfo + ) + ).toMatchSnapshot(); + }); + + it('returns no errors for valid package policy', () => { + expect( + validationHasErrors( + validatePackagePolicy( + VALID_AWS_POLICY as NewPackagePolicy, + (AWS_PACKAGE as unknown) as PackageInfo + ) + ) + ).toBe(false); + }); + }); +}); + +describe('Fleet - validationHasErrors()', () => { + it('returns true for stream validation results with errors', () => { + expect( + validationHasErrors({ + vars: { foo: ['foo error'], bar: null }, + }) + ).toBe(true); + }); + + it('returns false for stream validation results with no errors', () => { + expect( + validationHasErrors({ + vars: { foo: null, bar: null }, + }) + ).toBe(false); + }); + + it('returns true for input validation results with errors', () => { + expect( + validationHasErrors({ + vars: { foo: ['foo error'], bar: null }, + streams: { stream1: { vars: { foo: null, bar: null } } }, + }) + ).toBe(true); + expect( + validationHasErrors({ + vars: { foo: null, bar: null }, + streams: { stream1: { vars: { foo: ['foo error'], bar: null } } }, + }) + ).toBe(true); + }); + + it('returns false for input validation results with no errors', () => { + expect( + validationHasErrors({ + vars: { foo: null, bar: null }, + streams: { stream1: { vars: { foo: null, bar: null } } }, + }) + ).toBe(false); + }); + + it('returns true for package policy validation results with errors', () => { + expect( + validationHasErrors({ + name: ['name error'], + description: null, + namespace: null, + inputs: { + input1: { + vars: { foo: null, bar: null }, + streams: { stream1: { vars: { foo: null, bar: null } } }, + }, + }, + }) + ).toBe(true); + expect( + validationHasErrors({ + name: null, + description: null, + namespace: null, + inputs: { + input1: { + vars: { foo: ['foo error'], bar: null }, + streams: { stream1: { vars: { foo: null, bar: null } } }, + }, + }, + }) + ).toBe(true); + expect( + validationHasErrors({ + name: null, + description: null, + namespace: null, + inputs: { + input1: { + vars: { foo: null, bar: null }, + streams: { stream1: { vars: { foo: ['foo error'], bar: null } } }, + }, + }, + }) + ).toBe(true); + }); + + it('returns false for package policy validation results with no errors', () => { + expect( + validationHasErrors({ + name: null, + description: null, + namespace: null, + inputs: { + input1: { + vars: { foo: null, bar: null }, + streams: { stream1: { vars: { foo: null, bar: null } } }, + }, + }, + }) + ).toBe(false); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/services/validate_package_policy.ts b/x-pack/plugins/fleet/common/services/validate_package_policy.ts similarity index 76% rename from x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/services/validate_package_policy.ts rename to x-pack/plugins/fleet/common/services/validate_package_policy.ts index de1b8df9f959..b8673aa8b230 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/services/validate_package_policy.ts +++ b/x-pack/plugins/fleet/common/services/validate_package_policy.ts @@ -5,21 +5,22 @@ * 2.0. */ +import { getFlattenedObject } from '@kbn/std'; import { i18n } from '@kbn/i18n'; import { safeLoad } from 'js-yaml'; import { keyBy } from 'lodash'; -import { getFlattenedObject, isValidNamespace } from '../../../../services'; import type { NewPackagePolicy, PackagePolicyInput, PackagePolicyInputStream, PackagePolicyConfigRecordEntry, PackageInfo, - RegistryInput, RegistryStream, RegistryVarsEntry, -} from '../../../../types'; +} from '../types'; + +import { isValidNamespace, doesPackageHaveIntegrations } from './'; type Errors = string[] | null; @@ -48,6 +49,7 @@ export const validatePackagePolicy = ( packagePolicy: NewPackagePolicy, packageInfo: PackageInfo ): PackagePolicyValidationResults => { + const hasIntegrations = doesPackageHaveIntegrations(packageInfo); const validationResults: PackagePolicyValidationResults = { name: null, description: null, @@ -81,54 +83,61 @@ export const validatePackagePolicy = ( if ( !packageInfo.policy_templates || packageInfo.policy_templates.length === 0 || - !packageInfo.policy_templates[0] || - !packageInfo.policy_templates[0].inputs || - packageInfo.policy_templates[0].inputs.length === 0 + !packageInfo.policy_templates.find( + (policyTemplate) => policyTemplate.inputs && policyTemplate.inputs.length > 0 + ) ) { validationResults.inputs = null; return validationResults; } - const registryInputsByType: Record< - string, - RegistryInput - > = packageInfo.policy_templates[0].inputs.reduce((inputs, registryInput) => { - inputs[registryInput.type] = registryInput; - return inputs; - }, {} as Record); - - const registryStreamsByDataset: Record = ( - packageInfo.data_streams || [] - ).reduce((dataStreams, registryDataStream) => { - dataStreams[registryDataStream.dataset] = registryDataStream.streams || []; - return dataStreams; - }, {} as Record); - - // Validate each package policy input with either its own config fields or streams + // Build cache for fast var definition lookup + const inputVarDefsByPolicyTemplateAndType = packageInfo.policy_templates.reduce< + Record> + >((varDefs, policyTemplate) => { + (policyTemplate.inputs || []).forEach((input) => { + const varDefKey = hasIntegrations ? `${policyTemplate.name}-${input.type}` : input.type; + if ((input.vars || []).length) { + varDefs[varDefKey] = keyBy(input.vars || [], 'name'); + } + }); + return varDefs; + }, {}); + const streamsByDatasetAndInput = (packageInfo.data_streams || []).reduce< + Record + >((streams, dataStream) => { + dataStream.streams?.forEach((stream) => { + streams[`${dataStream.dataset}-${stream.input}`] = stream; + }); + return streams; + }, {}); + const streamVarDefsByDatasetAndInput = Object.entries(streamsByDatasetAndInput).reduce< + Record> + >((varDefs, [path, stream]) => { + varDefs[path] = keyBy(stream.vars || [], 'name'); + return varDefs; + }, {}); + + // Validate each package policy input with either its own var fields and stream vars packagePolicy.inputs.forEach((input) => { if (!input.vars && !input.streams) { return; } - + const inputKey = hasIntegrations ? `${input.policy_template}-${input.type}` : input.type; const inputValidationResults: PackagePolicyInputValidationResults = { vars: undefined, streams: {}, }; - const inputVarsByName = (registryInputsByType[input.type].vars || []).reduce( - (vars, registryVar) => { - vars[registryVar.name] = registryVar; - return vars; - }, - {} as Record - ); - - // Validate input-level config fields - const inputConfigs = Object.entries(input.vars || {}); - if (inputConfigs.length) { - inputValidationResults.vars = inputConfigs.reduce((results, [name, configEntry]) => { + // Validate input-level var fields + const inputVars = Object.entries(input.vars || {}); + if (inputVars.length) { + inputValidationResults.vars = inputVars.reduce((results, [name, configEntry]) => { results[name] = input.enabled - ? validatePackagePolicyConfig(configEntry, inputVarsByName[name]) + ? validatePackagePolicyConfig( + configEntry, + inputVarDefsByPolicyTemplateAndType[inputKey][name] + ) : null; return results; }, {} as ValidationEntry); @@ -136,28 +145,20 @@ export const validatePackagePolicy = ( delete inputValidationResults.vars; } - // Validate each input stream with config fields + // Validate each input stream with var definitions if (input.streams.length) { input.streams.forEach((stream) => { const streamValidationResults: PackagePolicyConfigValidationResults = {}; + const streamVarDefs = + streamVarDefsByDatasetAndInput[`${stream.data_stream.dataset}-${input.type}`]; // Validate stream-level config fields if (stream.vars) { - const streamVarsByName = ( - ( - registryStreamsByDataset[stream.data_stream.dataset].find( - (registryStream) => registryStream.input === input.type - ) || {} - ).vars || [] - ).reduce((vars, registryVar) => { - vars[registryVar.name] = registryVar; - return vars; - }, {} as Record); streamValidationResults.vars = Object.entries(stream.vars).reduce( (results, [name, configEntry]) => { results[name] = - input.enabled && stream.enabled - ? validatePackagePolicyConfig(configEntry, streamVarsByName[name]) + streamVarDefs[name] && input.enabled && stream.enabled + ? validatePackagePolicyConfig(configEntry, streamVarDefs[name]) : null; return results; }, @@ -172,7 +173,7 @@ export const validatePackagePolicy = ( } if (inputValidationResults.vars || inputValidationResults.streams) { - validationResults.inputs![input.type] = inputValidationResults; + validationResults.inputs![inputKey] = inputValidationResults; } }); diff --git a/x-pack/plugins/fleet/common/types/models/package_policy.ts b/x-pack/plugins/fleet/common/types/models/package_policy.ts index c0b74c2a7b02..40c3a0c66f15 100644 --- a/x-pack/plugins/fleet/common/types/models/package_policy.ts +++ b/x-pack/plugins/fleet/common/types/models/package_policy.ts @@ -37,6 +37,7 @@ export interface PackagePolicyInputStream extends NewPackagePolicyInputStream { export interface NewPackagePolicyInput { type: string; + policy_template?: string; enabled: boolean; keep_enabled?: boolean; vars?: PackagePolicyConfigRecord; diff --git a/x-pack/plugins/fleet/public/applications/fleet/hooks/use_breadcrumbs.tsx b/x-pack/plugins/fleet/public/applications/fleet/hooks/use_breadcrumbs.tsx index eeb8f18d17d4..fd980475dc91 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/hooks/use_breadcrumbs.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/hooks/use_breadcrumbs.tsx @@ -91,10 +91,10 @@ const breadcrumbGetters: { }), }, ], - add_integration_to_policy: ({ pkgTitle, pkgkey }) => [ + add_integration_to_policy: ({ pkgTitle, pkgkey, integration }) => [ INTEGRATIONS_BASE_BREADCRUMB, { - href: pagePathGetters.integration_details_overview({ pkgkey })[1], + href: pagePathGetters.integration_details_overview({ pkgkey, integration })[1], text: pkgTitle, useIntegrationsBasePath: true, }, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/layout.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/layout.tsx index d2bc49bdf00e..060c49a84c5a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/layout.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/layout.tsx @@ -19,7 +19,7 @@ import { } from '@elastic/eui'; import { WithHeaderLayout } from '../../../../layouts'; -import type { AgentPolicy, PackageInfo } from '../../../../types'; +import type { AgentPolicy, PackageInfo, RegistryPolicyTemplate } from '../../../../types'; import { PackageIcon } from '../../../../components'; import type { CreatePackagePolicyFrom } from '../types'; @@ -29,6 +29,7 @@ export const CreatePackagePolicyPageLayout: React.FunctionComponent<{ onCancel?: React.ReactEventHandler; agentPolicy?: AgentPolicy; packageInfo?: PackageInfo; + integrationInfo?: RegistryPolicyTemplate; 'data-test-subj'?: string; }> = memo( ({ @@ -37,6 +38,7 @@ export const CreatePackagePolicyPageLayout: React.FunctionComponent<{ onCancel, agentPolicy, packageInfo, + integrationInfo, children, 'data-test-subj': dataTestSubj, }) => { @@ -47,8 +49,9 @@ export const CreatePackagePolicyPageLayout: React.FunctionComponent<{ @@ -68,7 +71,7 @@ export const CreatePackagePolicyPageLayout: React.FunctionComponent<{ id="xpack.fleet.createPackagePolicy.pageTitleWithPackageName" defaultMessage="Add {packageName} integration" values={{ - packageName: packageInfo.title, + packageName: integrationInfo?.title || packageInfo.title, }} /> )} @@ -98,7 +101,14 @@ export const CreatePackagePolicyPageLayout: React.FunctionComponent<{ ); - }, [dataTestSubj, from, packageInfo]); + }, [ + dataTestSubj, + from, + integrationInfo?.icons, + integrationInfo?.name, + integrationInfo?.title, + packageInfo, + ]); const pageDescription = useMemo(() => { return from === 'edit' || from === 'package-edit' ? ( 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 6563918a5efb..3e4b120a28f8 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 @@ -60,19 +60,26 @@ const StepsWithLessPadding = styled(EuiSteps)` } `; +interface AddToPolicyParams { + pkgkey: string; + integration?: string; +} + +interface AddFromPolicyParams { + policyId: string; +} + export const CreatePackagePolicyPage: React.FunctionComponent = () => { const { notifications } = useStartServices(); const { agents: { enabled: isFleetEnabled }, } = useConfig(); - const { - params: { policyId, pkgkey }, - } = useRouteMatch<{ policyId: string; pkgkey: string }>(); + const { params } = useRouteMatch(); const { getHref, getPath } = useLink(); const history = useHistory(); const handleNavigateTo = useNavigateToCallback(); const routeState = useIntraAppState(); - const from: CreatePackagePolicyFrom = policyId ? 'policy' : 'package'; + const from: CreatePackagePolicyFrom = 'policyId' in params ? 'policy' : 'package'; // Agent policy and package info states const [agentPolicy, setAgentPolicy] = useState(); @@ -215,9 +222,11 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { return routeState.onCancelUrl; } return from === 'policy' - ? getHref('policy_details', { policyId: agentPolicyId || policyId }) - : getHref('integration_details_overview', { pkgkey }); - }, [agentPolicyId, policyId, from, getHref, pkgkey, routeState]); + ? getHref('policy_details', { + policyId: agentPolicyId || (params as AddFromPolicyParams).policyId, + }) + : getHref('integration_details_overview', { pkgkey: (params as AddToPolicyParams).pkgkey }); + }, [agentPolicyId, params, from, getHref, routeState]); const cancelClickHandler: ReactEventHandler = useCallback( (ev) => { @@ -230,14 +239,14 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { ); // Save package policy - const savePackagePolicy = async () => { + const savePackagePolicy = useCallback(async () => { setFormState('LOADING'); const result = await sendCreatePackagePolicy(packagePolicy); setFormState('SUBMITTED'); return result; - }; + }, [packagePolicy]); - const onSubmit = async () => { + const onSubmit = useCallback(async () => { if (formState === 'VALID' && hasErrors) { setFormState('INVALID'); return; @@ -255,7 +264,11 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { : routeState.onSaveNavigateTo ); } else { - history.push(getPath('policy_details', { policyId: agentPolicy?.id || policyId })); + history.push( + getPath('policy_details', { + policyId: agentPolicy?.id || (params as AddFromPolicyParams).policyId, + }) + ); } notifications.toasts.addSuccess({ @@ -282,27 +295,54 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { }); setFormState('VALID'); } - }; - - const layoutProps = { - from, - cancelUrl, - onCancel: cancelClickHandler, + }, [ + agentCount, agentPolicy, - packageInfo, - }; + formState, + getPath, + handleNavigateTo, + hasErrors, + history, + notifications.toasts, + packagePolicy.name, + params, + routeState, + savePackagePolicy, + ]); + + const integrationInfo = useMemo( + () => + (params as AddToPolicyParams).integration + ? packageInfo?.policy_templates?.find( + (policyTemplate) => policyTemplate.name === (params as AddToPolicyParams).integration + ) + : undefined, + [packageInfo?.policy_templates, params] + ); + + const layoutProps = useMemo( + () => ({ + from, + cancelUrl, + onCancel: cancelClickHandler, + agentPolicy, + packageInfo, + integrationInfo, + }), + [agentPolicy, cancelClickHandler, cancelUrl, from, integrationInfo, packageInfo] + ); const stepSelectAgentPolicy = useMemo( () => ( ), - [pkgkey, updatePackageInfo, agentPolicy, updateAgentPolicy] + [params, updatePackageInfo, agentPolicy, updateAgentPolicy] ); const ExtensionView = useUIExtension(packagePolicy.package?.name ?? '', 'package-policy-create'); @@ -310,14 +350,14 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { const stepSelectPackage = useMemo( () => ( ), - [policyId, updateAgentPolicy, packageInfo, updatePackageInfo] + [params, updateAgentPolicy, packageInfo, updatePackageInfo] ); const stepConfigurePackagePolicy = useMemo( @@ -333,12 +373,14 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { updatePackagePolicy={updatePackagePolicy} validationResults={validationResults!} submitAttempted={formState === 'INVALID'} + integrationToEnable={integrationInfo?.name} /> {/* Only show the out-of-box configuration step if a UI extension is NOT registered */} {!ExtensionView && ( { updatePackagePolicy, validationResults, formState, + integrationInfo?.name, ExtensionView, handleExtensionViewOnChange, ] @@ -406,8 +449,9 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { {from === 'package' ? packageInfo && ( ) : agentPolicy && ( @@ -476,8 +520,13 @@ const PolicyBreadcrumb: React.FunctionComponent<{ const IntegrationBreadcrumb: React.FunctionComponent<{ pkgTitle: string; pkgkey: string; -}> = ({ pkgTitle, pkgkey }) => { - useBreadcrumbs('add_integration_to_policy', { pkgTitle, pkgkey }); + integration?: string; +}> = ({ pkgTitle, pkgkey, integration }) => { + useBreadcrumbs('add_integration_to_policy', { + pkgTitle, + pkgkey, + ...(integration ? { integration } : {}), + }); return null; }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/services/index.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/services/index.ts index 0c2dc6c1c649..0e1953316fd5 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/services/index.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/services/index.ts @@ -15,4 +15,4 @@ export { validatePackagePolicyConfig, validationHasErrors, countValidationErrors, -} from './validate_package_policy'; +} from '../../../../services'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/services/validate_package_policy.test.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/services/validate_package_policy.test.ts deleted file mode 100644 index 180a585a4305..000000000000 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/services/validate_package_policy.test.ts +++ /dev/null @@ -1,600 +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 { installationStatuses } from '../../../../../../../common/constants'; -import type { PackageInfo, NewPackagePolicy, RegistryPolicyTemplate } from '../../../../types'; - -import { validatePackagePolicy, validationHasErrors } from './validate_package_policy'; - -describe('Fleet - validatePackagePolicy()', () => { - const mockPackage = ({ - name: 'mock-package', - title: 'Mock package', - version: '0.0.0', - description: 'description', - type: 'mock', - categories: [], - requirement: { kibana: { versions: '' }, elasticsearch: { versions: '' } }, - format_version: '', - download: '', - path: '', - assets: { - kibana: { - dashboard: [], - visualization: [], - search: [], - 'index-pattern': [], - }, - }, - status: installationStatuses.NotInstalled, - data_streams: [ - { - dataset: 'foo', - streams: [ - { - input: 'foo', - title: 'Foo', - vars: [{ name: 'var-name', type: 'yaml' }], - }, - ], - }, - { - dataset: 'bar', - streams: [ - { - input: 'bar', - title: 'Bar', - vars: [{ name: 'var-name', type: 'yaml', required: true }], - }, - { - input: 'with-no-stream-vars', - title: 'Bar stream no vars', - enabled: true, - }, - ], - }, - { - dataset: 'bar2', - streams: [ - { - input: 'bar', - title: 'Bar 2', - vars: [{ default: 'bar2-var-value', name: 'var-name', type: 'text' }], - }, - ], - }, - { - dataset: 'disabled', - streams: [ - { - input: 'with-disabled-streams', - title: 'Disabled', - enabled: false, - vars: [{ multi: true, required: true, name: 'var-name', type: 'text' }], - }, - ], - }, - { - dataset: 'disabled2', - streams: [ - { - input: 'with-disabled-streams', - title: 'Disabled 2', - enabled: false, - }, - ], - }, - ], - policy_templates: [ - { - name: 'pkgPolicy1', - title: 'Package policy 1', - description: 'test package policy', - inputs: [ - { - type: 'foo', - title: 'Foo', - vars: [ - { default: 'foo-input-var-value', name: 'foo-input-var-name', type: 'text' }, - { - default: 'foo-input2-var-value', - name: 'foo-input2-var-name', - required: true, - type: 'text', - }, - { name: 'foo-input3-var-name', type: 'text', required: true, multi: true }, - ], - }, - { - type: 'bar', - title: 'Bar', - vars: [ - { - default: ['value1', 'value2'], - name: 'bar-input-var-name', - type: 'text', - multi: true, - }, - { name: 'bar-input2-var-name', required: true, type: 'text' }, - ], - }, - { - type: 'with-no-config-or-streams', - title: 'With no config or streams', - }, - { - type: 'with-disabled-streams', - title: 'With disabled streams', - }, - { - type: 'with-no-stream-vars', - enabled: true, - vars: [{ required: true, name: 'var-name', type: 'text' }], - }, - ], - }, - ], - } as unknown) as PackageInfo; - - const validPackagePolicy: NewPackagePolicy = { - name: 'pkgPolicy1-1', - namespace: 'default', - policy_id: 'test-policy', - enabled: true, - output_id: 'test-output', - inputs: [ - { - type: 'foo', - enabled: true, - vars: { - 'foo-input-var-name': { value: 'foo-input-var-value', type: 'text' }, - 'foo-input2-var-name': { value: 'foo-input2-var-value', type: 'text' }, - 'foo-input3-var-name': { value: ['test'], type: 'text' }, - }, - streams: [ - { - data_stream: { dataset: 'foo', type: 'logs' }, - enabled: true, - vars: { 'var-name': { value: 'test_yaml: value', type: 'yaml' } }, - }, - ], - }, - { - type: 'bar', - enabled: true, - vars: { - 'bar-input-var-name': { value: ['value1', 'value2'], type: 'text' }, - 'bar-input2-var-name': { value: 'test', type: 'text' }, - }, - streams: [ - { - data_stream: { dataset: 'bar', type: 'logs' }, - enabled: true, - vars: { 'var-name': { value: 'test_yaml: value', type: 'yaml' } }, - }, - { - data_stream: { dataset: 'bar2', type: 'logs' }, - enabled: true, - vars: { 'var-name': { value: undefined, type: 'text' } }, - }, - ], - }, - { - type: 'with-no-config-or-streams', - enabled: true, - streams: [], - }, - { - type: 'with-disabled-streams', - enabled: true, - streams: [ - { - data_stream: { dataset: 'disabled', type: 'logs' }, - enabled: false, - vars: { 'var-name': { value: undefined, type: 'text' } }, - }, - { - data_stream: { dataset: 'disabled2', type: 'logs' }, - enabled: false, - }, - ], - }, - { - type: 'with-no-stream-vars', - enabled: true, - vars: { - 'var-name': { value: 'test', type: 'text' }, - }, - streams: [ - { - data_stream: { dataset: 'with-no-stream-vars-bar', type: 'logs' }, - enabled: true, - }, - ], - }, - ], - }; - - const invalidPackagePolicy: NewPackagePolicy = { - ...validPackagePolicy, - name: '', - inputs: [ - { - type: 'foo', - enabled: true, - vars: { - 'foo-input-var-name': { value: undefined, type: 'text' }, - 'foo-input2-var-name': { value: '', type: 'text' }, - 'foo-input3-var-name': { value: [], type: 'text' }, - }, - streams: [ - { - data_stream: { dataset: 'foo', type: 'logs' }, - enabled: true, - vars: { 'var-name': { value: 'invalidyaml: test\n foo bar:', type: 'yaml' } }, - }, - ], - }, - { - type: 'bar', - enabled: true, - vars: { - 'bar-input-var-name': { value: 'invalid value for multi', type: 'text' }, - 'bar-input2-var-name': { value: undefined, type: 'text' }, - }, - streams: [ - { - data_stream: { dataset: 'bar', type: 'logs' }, - enabled: true, - vars: { 'var-name': { value: ' \n\n', type: 'yaml' } }, - }, - { - data_stream: { dataset: 'bar2', type: 'logs' }, - enabled: true, - vars: { 'var-name': { value: undefined, type: 'text' } }, - }, - ], - }, - { - type: 'with-no-config-or-streams', - enabled: true, - streams: [], - }, - { - type: 'with-disabled-streams', - enabled: true, - streams: [ - { - data_stream: { dataset: 'disabled', type: 'logs' }, - enabled: false, - vars: { - 'var-name': { - value: 'invalid value but not checked due to not enabled', - type: 'text', - }, - }, - }, - { - data_stream: { dataset: 'disabled2', type: 'logs' }, - enabled: false, - }, - ], - }, - { - type: 'with-no-stream-vars', - enabled: true, - vars: { - 'var-name': { value: undefined, type: 'text' }, - }, - streams: [ - { - data_stream: { dataset: 'with-no-stream-vars-bar', type: 'logs' }, - enabled: true, - }, - ], - }, - ], - }; - - const noErrorsValidationResults = { - name: null, - description: null, - namespace: null, - inputs: { - foo: { - vars: { - 'foo-input-var-name': null, - 'foo-input2-var-name': null, - 'foo-input3-var-name': null, - }, - streams: { foo: { vars: { 'var-name': null } } }, - }, - bar: { - vars: { 'bar-input-var-name': null, 'bar-input2-var-name': null }, - streams: { - bar: { vars: { 'var-name': null } }, - bar2: { vars: { 'var-name': null } }, - }, - }, - 'with-disabled-streams': { - streams: { - disabled: { - vars: { 'var-name': null }, - }, - disabled2: {}, - }, - }, - 'with-no-stream-vars': { - streams: { - 'with-no-stream-vars-bar': {}, - }, - vars: { 'var-name': null }, - }, - }, - }; - - it('returns no errors for valid package policy', () => { - expect(validatePackagePolicy(validPackagePolicy, mockPackage)).toEqual( - noErrorsValidationResults - ); - }); - - it('returns errors for invalid package policy', () => { - expect(validatePackagePolicy(invalidPackagePolicy, mockPackage)).toEqual({ - name: ['Name is required'], - description: null, - namespace: null, - inputs: { - foo: { - vars: { - 'foo-input-var-name': null, - 'foo-input2-var-name': ['foo-input2-var-name is required'], - 'foo-input3-var-name': ['foo-input3-var-name is required'], - }, - streams: { foo: { vars: { 'var-name': ['Invalid YAML format'] } } }, - }, - bar: { - vars: { - 'bar-input-var-name': ['Invalid format'], - 'bar-input2-var-name': ['bar-input2-var-name is required'], - }, - streams: { - bar: { vars: { 'var-name': ['var-name is required'] } }, - bar2: { vars: { 'var-name': null } }, - }, - }, - 'with-disabled-streams': { - streams: { - disabled: { vars: { 'var-name': null } }, - disabled2: {}, - }, - }, - 'with-no-stream-vars': { - vars: { - 'var-name': ['var-name is required'], - }, - streams: { 'with-no-stream-vars-bar': {} }, - }, - }, - }); - }); - - it('returns no errors for disabled inputs', () => { - const disabledInputs = invalidPackagePolicy.inputs.map((input) => ({ - ...input, - enabled: false, - })); - expect( - validatePackagePolicy({ ...validPackagePolicy, inputs: disabledInputs }, mockPackage) - ).toEqual(noErrorsValidationResults); - }); - - it('returns only package policy and input-level errors for disabled streams', () => { - const inputsWithDisabledStreams = invalidPackagePolicy.inputs.map((input) => - input.streams - ? { - ...input, - streams: input.streams.map((stream) => ({ ...stream, enabled: false })), - } - : input - ); - expect( - validatePackagePolicy( - { ...invalidPackagePolicy, inputs: inputsWithDisabledStreams }, - mockPackage - ) - ).toEqual({ - name: ['Name is required'], - description: null, - namespace: null, - inputs: { - foo: { - vars: { - 'foo-input-var-name': null, - 'foo-input2-var-name': ['foo-input2-var-name is required'], - 'foo-input3-var-name': ['foo-input3-var-name is required'], - }, - streams: { foo: { vars: { 'var-name': null } } }, - }, - bar: { - vars: { - 'bar-input-var-name': ['Invalid format'], - 'bar-input2-var-name': ['bar-input2-var-name is required'], - }, - streams: { - bar: { vars: { 'var-name': null } }, - bar2: { vars: { 'var-name': null } }, - }, - }, - 'with-disabled-streams': { - streams: { - disabled: { - vars: { 'var-name': null }, - }, - disabled2: {}, - }, - }, - 'with-no-stream-vars': { - vars: { - 'var-name': ['var-name is required'], - }, - streams: { 'with-no-stream-vars-bar': {} }, - }, - }, - }); - }); - - it('returns no errors for packages with no package policies', () => { - expect( - validatePackagePolicy(validPackagePolicy, { - ...mockPackage, - policy_templates: undefined, - }) - ).toEqual({ - name: null, - description: null, - namespace: null, - inputs: null, - }); - expect( - validatePackagePolicy(validPackagePolicy, { - ...mockPackage, - policy_templates: [], - }) - ).toEqual({ - name: null, - description: null, - namespace: null, - inputs: null, - }); - }); - - it('returns no errors for packages with no inputs', () => { - expect( - validatePackagePolicy(validPackagePolicy, { - ...mockPackage, - policy_templates: [{} as RegistryPolicyTemplate], - }) - ).toEqual({ - name: null, - description: null, - namespace: null, - inputs: null, - }); - expect( - validatePackagePolicy(validPackagePolicy, { - ...mockPackage, - policy_templates: [({ inputs: [] } as unknown) as RegistryPolicyTemplate], - }) - ).toEqual({ - name: null, - description: null, - namespace: null, - inputs: null, - }); - }); -}); - -describe('Fleet - validationHasErrors()', () => { - it('returns true for stream validation results with errors', () => { - expect( - validationHasErrors({ - vars: { foo: ['foo error'], bar: null }, - }) - ).toBe(true); - }); - - it('returns false for stream validation results with no errors', () => { - expect( - validationHasErrors({ - vars: { foo: null, bar: null }, - }) - ).toBe(false); - }); - - it('returns true for input validation results with errors', () => { - expect( - validationHasErrors({ - vars: { foo: ['foo error'], bar: null }, - streams: { stream1: { vars: { foo: null, bar: null } } }, - }) - ).toBe(true); - expect( - validationHasErrors({ - vars: { foo: null, bar: null }, - streams: { stream1: { vars: { foo: ['foo error'], bar: null } } }, - }) - ).toBe(true); - }); - - it('returns false for input validation results with no errors', () => { - expect( - validationHasErrors({ - vars: { foo: null, bar: null }, - streams: { stream1: { vars: { foo: null, bar: null } } }, - }) - ).toBe(false); - }); - - it('returns true for package policy validation results with errors', () => { - expect( - validationHasErrors({ - name: ['name error'], - description: null, - namespace: null, - inputs: { - input1: { - vars: { foo: null, bar: null }, - streams: { stream1: { vars: { foo: null, bar: null } } }, - }, - }, - }) - ).toBe(true); - expect( - validationHasErrors({ - name: null, - description: null, - namespace: null, - inputs: { - input1: { - vars: { foo: ['foo error'], bar: null }, - streams: { stream1: { vars: { foo: null, bar: null } } }, - }, - }, - }) - ).toBe(true); - expect( - validationHasErrors({ - name: null, - description: null, - namespace: null, - inputs: { - input1: { - vars: { foo: null, bar: null }, - streams: { stream1: { vars: { foo: ['foo error'], bar: null } } }, - }, - }, - }) - ).toBe(true); - }); - - it('returns false for package policy validation results with no errors', () => { - expect( - validationHasErrors({ - name: null, - description: null, - namespace: null, - inputs: { - input1: { - vars: { foo: null, bar: null }, - streams: { stream1: { vars: { foo: null, bar: null } } }, - }, - }, - }) - ).toBe(false); - }); -}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_configure_package.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_configure_package.tsx index e64598e583d3..1ff5d20baec0 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_configure_package.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_configure_package.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; import { EuiHorizontalRule, EuiFlexGroup, @@ -15,86 +15,92 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { - PackageInfo, - RegistryStream, - NewPackagePolicy, - NewPackagePolicyInput, -} from '../../../types'; +import type { PackageInfo, NewPackagePolicy, NewPackagePolicyInput } from '../../../types'; import { Loading } from '../../../components'; +import { getStreamsForInputType, doesPackageHaveIntegrations } from '../../../services'; import type { PackagePolicyValidationResults } from './services'; import { PackagePolicyInputPanel } from './components'; -const findStreamsForInputType = ( - inputType: string, - packageInfo: PackageInfo -): Array => { - const streams: Array = []; - - (packageInfo.data_streams || []).forEach((dataStream) => { - (dataStream.streams || []).forEach((stream) => { - if (stream.input === inputType) { - streams.push({ - ...stream, - data_stream: { - dataset: dataStream.dataset, - }, - }); - } - }); - }); - - return streams; -}; - export const StepConfigurePackagePolicy: React.FunctionComponent<{ packageInfo: PackageInfo; + showOnlyIntegration?: string; packagePolicy: NewPackagePolicy; updatePackagePolicy: (fields: Partial) => void; validationResults: PackagePolicyValidationResults; submitAttempted: boolean; -}> = ({ packageInfo, packagePolicy, updatePackagePolicy, validationResults, submitAttempted }) => { +}> = ({ + packageInfo, + showOnlyIntegration, + packagePolicy, + updatePackagePolicy, + validationResults, + submitAttempted, +}) => { + const hasIntegrations = useMemo(() => doesPackageHaveIntegrations(packageInfo), [packageInfo]); + const packagePolicyTemplates = useMemo( + () => + showOnlyIntegration + ? (packageInfo.policy_templates || []).filter( + (policyTemplate) => policyTemplate.name === showOnlyIntegration + ) + : packageInfo.policy_templates || [], + [packageInfo.policy_templates, showOnlyIntegration] + ); + // Configure inputs (and their streams) // Assume packages only export one config template for now const renderConfigureInputs = () => - packageInfo.policy_templates && - packageInfo.policy_templates[0] && - packageInfo.policy_templates[0].inputs && - packageInfo.policy_templates[0].inputs.length ? ( + packagePolicyTemplates.length ? ( <> - {packageInfo.policy_templates[0].inputs.map((packageInput) => { - const packagePolicyInput = packagePolicy.inputs.find( - (input) => input.type === packageInput.type - ); - const packageInputStreams = findStreamsForInputType(packageInput.type, packageInfo); - return packagePolicyInput ? ( - - ) => { - const indexOfUpdatedInput = packagePolicy.inputs.findIndex( - (input) => input.type === packageInput.type - ); - const newInputs = [...packagePolicy.inputs]; - newInputs[indexOfUpdatedInput] = { - ...newInputs[indexOfUpdatedInput], - ...updatedInput, - }; - updatePackagePolicy({ - inputs: newInputs, - }); - }} - inputValidationResults={validationResults!.inputs![packagePolicyInput.type]} - forceShowErrors={submitAttempted} - /> - - - ) : null; + {packagePolicyTemplates.map((policyTemplate) => { + return (policyTemplate.inputs || []).map((packageInput) => { + const packagePolicyInput = packagePolicy.inputs.find( + (input) => + input.type === packageInput.type && + (hasIntegrations ? input.policy_template === policyTemplate.name : true) + ); + const packageInputStreams = getStreamsForInputType( + packageInput.type, + packageInfo, + hasIntegrations ? policyTemplate.data_streams : [] + ); + return packagePolicyInput ? ( + + ) => { + const indexOfUpdatedInput = packagePolicy.inputs.findIndex( + (input) => + input.type === packageInput.type && + (hasIntegrations ? input.policy_template === policyTemplate.name : true) + ); + const newInputs = [...packagePolicy.inputs]; + newInputs[indexOfUpdatedInput] = { + ...newInputs[indexOfUpdatedInput], + ...updatedInput, + }; + updatePackagePolicy({ + inputs: newInputs, + }); + }} + inputValidationResults={ + validationResults!.inputs![ + hasIntegrations + ? `${policyTemplate.name}-${packagePolicyInput.type}` + : packagePolicyInput.type + ] + } + forceShowErrors={submitAttempted} + /> + + + ) : null; + }); })} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_define_package_policy.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_define_package_policy.tsx index 380e49a1d8dd..7444bed6ed3f 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_define_package_policy.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_define_package_policy.tsx @@ -38,7 +38,7 @@ export const StepDefinePackagePolicy: React.FunctionComponent<{ agentPolicy: AgentPolicy; packageInfo: PackageInfo; packagePolicy: NewPackagePolicy; - integration?: string; + integrationToEnable?: string; updatePackagePolicy: (fields: Partial) => void; validationResults: PackagePolicyValidationResults; submitAttempted: boolean; @@ -47,7 +47,7 @@ export const StepDefinePackagePolicy: React.FunctionComponent<{ agentPolicy, packageInfo, packagePolicy, - integration, + integrationToEnable, updatePackagePolicy, validationResults, submitAttempted, @@ -95,7 +95,8 @@ export const StepDefinePackagePolicy: React.FunctionComponent<{ ? pkgPoliciesWithMatchingNames[pkgPoliciesWithMatchingNames.length - 1] + 1 : 1 }`, - packagePolicy.description + packagePolicy.description, + integrationToEnable ) ); } @@ -107,7 +108,7 @@ export const StepDefinePackagePolicy: React.FunctionComponent<{ namespace: agentPolicy.namespace, }); } - }, [packagePolicy, agentPolicy, packageInfo, updatePackagePolicy, integration]); + }, [packagePolicy, agentPolicy, packageInfo, updatePackagePolicy, integrationToEnable]); return validationResults ? ( { ], policy_templates: [ { + name: 'template_1', + inputs: [{ type: 'log', template_path: 'some_template_path.yml' }], + }, + { + name: 'template_2', inputs: [{ type: 'log', template_path: 'some_template_path.yml' }], }, ], @@ -416,6 +421,7 @@ describe('Package policy service', () => { [ { type: 'log', + policy_template: 'template_1', enabled: true, vars: { hosts: { @@ -433,12 +439,24 @@ describe('Package policy service', () => { }, ], }, + { + type: 'log', + policy_template: 'template_2', + enabled: true, + vars: { + hosts: { + value: ['localhost'], + }, + }, + streams: [], + }, ] ); expect(inputs).toEqual([ { type: 'log', + policy_template: 'template_1', enabled: true, vars: { hosts: { @@ -465,6 +483,20 @@ describe('Package policy service', () => { }, ], }, + { + type: 'log', + policy_template: 'template_2', + enabled: true, + vars: { + hosts: { + value: ['localhost'], + }, + }, + compiled_input: { + hosts: ['localhost'], + }, + streams: [], + }, ]); }); diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index 02f9731421ba..93bcef458279 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -511,11 +511,17 @@ async function _compilePackagePolicyInput( vars: PackagePolicy['vars'], input: PackagePolicyInput ) { - if ((!input.enabled || !pkgInfo.policy_templates?.[0]?.inputs?.length) ?? 0 > 0) { + const packagePolicyTemplate = input.policy_template + ? pkgInfo.policy_templates?.find( + (policyTemplate) => policyTemplate.name === input.policy_template + ) + : pkgInfo.policy_templates?.[0]; + + if (!input.enabled || !packagePolicyTemplate || !packagePolicyTemplate.inputs?.length) { return undefined; } - const packageInputs = pkgInfo.policy_templates[0].inputs; + const packageInputs = packagePolicyTemplate.inputs; const packageInput = packageInputs.find((pkgInput) => pkgInput.type === input.type); if (!packageInput) { throw new Error(`Input template not found, unable to find input type ${input.type}`); 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 3735cfffeaa7..e69e38c18728 100644 --- a/x-pack/plugins/fleet/server/types/models/package_policy.ts +++ b/x-pack/plugins/fleet/server/types/models/package_policy.ts @@ -45,6 +45,7 @@ const PackagePolicyBaseSchema = { inputs: schema.arrayOf( schema.object({ type: schema.string(), + policy_template: schema.maybe(schema.string()), enabled: schema.boolean(), keep_enabled: schema.maybe(schema.boolean()), vars: schema.maybe(ConfigRecordSchema),