From 2f9392e09110bd739b89ea787d9dfca8a70c79fc Mon Sep 17 00:00:00 2001 From: Richard Gomez <32133502+rgmz@users.noreply.github.com> Date: Wed, 24 Jun 2020 22:09:09 -0400 Subject: [PATCH 01/11] feat: include alias path when generating template --- scripts/generators/es_template.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/generators/es_template.py b/scripts/generators/es_template.py index 5bf264a784..3d6c349910 100644 --- a/scripts/generators/es_template.py +++ b/scripts/generators/es_template.py @@ -59,6 +59,11 @@ def entry_for(field): ecs_helpers.dict_copy_existing_keys(field, field_entry, ['ignore_above']) elif field['type'] == 'text': ecs_helpers.dict_copy_existing_keys(field, field_entry, ['norms']) + elif field['type'] == 'alias': + if 'path' in field: + field_entry['path'] = field['path'] + else: + raise ValueError(f'The [path] property must be specified for field [{field["name"]}]') if 'multi_fields' in field: field_entry['fields'] = {} From 81109da8e038105d67411a2b15e058c7f12f7f0b Mon Sep 17 00:00:00 2001 From: Richard Gomez Date: Sun, 4 Oct 2020 12:14:38 -0400 Subject: [PATCH 02/11] feat(schema): ensure 'alias' has 'path' field --- scripts/schema/cleaner.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/schema/cleaner.py b/scripts/schema/cleaner.py index 5f15d459fe..8eced6390c 100644 --- a/scripts/schema/cleaner.py +++ b/scripts/schema/cleaner.py @@ -158,6 +158,12 @@ def field_mandatory_attributes(field): return current_field_attributes = sorted(field['field_details'].keys()) missing_attributes = ecs_helpers.list_subtract(FIELD_MANDATORY_ATTRIBUTES, current_field_attributes) + + # The `alias` type requires a target path. + # https://www.elastic.co/guide/en/elasticsearch/reference/current/alias.html + if field['field_details']['type'] == 'alias' and 'path' not in current_field_attributes: + missing_attributes.append('path') + if len(missing_attributes) > 0: msg = "Field is missing the following mandatory attributes: {}.\nFound these: {}.\nField details: {}" raise ValueError(msg.format(', '.join(missing_attributes), From 765057e0fa8ef846dd306c4bcb0ef1dd7589d673 Mon Sep 17 00:00:00 2001 From: Richard Gomez Date: Sun, 4 Oct 2020 12:36:04 -0400 Subject: [PATCH 03/11] feat(generators): include 'path' in beats output --- scripts/generators/beats.py | 8 ++++++++ scripts/schema/cleaner.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/scripts/generators/beats.py b/scripts/generators/beats.py index f305261407..64d14b5d54 100644 --- a/scripts/generators/beats.py +++ b/scripts/generators/beats.py @@ -62,6 +62,14 @@ def fieldset_field_array(source_fields, df_whitelist, fieldset_prefix): if not ecs_field['flat_name'] in df_whitelist: beats_field['default_field'] = False + # Include the `path` attribute for `alias` types. + # https://github.com/elastic/ecs/issues/876 + if ecs_field['type'] == 'alias': + if 'path' in ecs_field: + beats_field['path'] = ecs_field['path'] + else: + raise ValueError(f'The [path] property must be specified for field [{contextual_name}]') + fields.append(beats_field) return sorted(fields, key=lambda x: x['name']) diff --git a/scripts/schema/cleaner.py b/scripts/schema/cleaner.py index 8eced6390c..6f67257134 100644 --- a/scripts/schema/cleaner.py +++ b/scripts/schema/cleaner.py @@ -160,7 +160,7 @@ def field_mandatory_attributes(field): missing_attributes = ecs_helpers.list_subtract(FIELD_MANDATORY_ATTRIBUTES, current_field_attributes) # The `alias` type requires a target path. - # https://www.elastic.co/guide/en/elasticsearch/reference/current/alias.html + # https://github.com/elastic/ecs/issues/876 if field['field_details']['type'] == 'alias' and 'path' not in current_field_attributes: missing_attributes.append('path') From b30a409960a6e4733b61a4c75d8300adf0c51f9d Mon Sep 17 00:00:00 2001 From: Richard Gomez Date: Sun, 4 Oct 2020 12:52:52 -0400 Subject: [PATCH 04/11] fix(schema): safely get 'type' to avoid exception --- scripts/schema/cleaner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/schema/cleaner.py b/scripts/schema/cleaner.py index 6f67257134..aebda18ae5 100644 --- a/scripts/schema/cleaner.py +++ b/scripts/schema/cleaner.py @@ -161,7 +161,7 @@ def field_mandatory_attributes(field): # The `alias` type requires a target path. # https://github.com/elastic/ecs/issues/876 - if field['field_details']['type'] == 'alias' and 'path' not in current_field_attributes: + if field['field_details'].get('type') == 'alias' and 'path' not in current_field_attributes: missing_attributes.append('path') if len(missing_attributes) > 0: From e3a61a39b859479233f47108115a09326f586f51 Mon Sep 17 00:00:00 2001 From: Richard Gomez Date: Sun, 4 Oct 2020 13:03:05 -0400 Subject: [PATCH 05/11] test: add coverage for 'alias' type --- scripts/tests/test_es_template.py | 23 +++++++++++++++++++++++ scripts/tests/unit/test_schema_cleaner.py | 7 +++++++ 2 files changed, 30 insertions(+) diff --git a/scripts/tests/test_es_template.py b/scripts/tests/test_es_template.py index 9ff4c30306..ada4e9bba6 100644 --- a/scripts/tests/test_es_template.py +++ b/scripts/tests/test_es_template.py @@ -110,5 +110,28 @@ def test_entry_for_index(self): self.assertEqual(es_template.entry_for(test_map), exp) + def test_entry_for_alias(self): + test_map = { + 'name': 'test.alias', + 'type': 'alias', + 'path': 'alias.target' + } + + exp = { + 'type': 'alias', + 'path': 'alias.target' + } + self.assertEqual(es_template.entry_for(test_map), exp) + + def test_entry_for_alias_missing_path(self): + test_map = { + 'name': 'test.alias', + 'type': 'alias' + } + + with self.assertRaisesRegex(ValueError, + "The \[path\] property must be specified for field \[{}\]".format(test_map['name'])): + es_template.entry_for(test_map) + if __name__ == '__main__': unittest.main() diff --git a/scripts/tests/unit/test_schema_cleaner.py b/scripts/tests/unit/test_schema_cleaner.py index 8298a32bb3..b159b9ff3d 100644 --- a/scripts/tests/unit/test_schema_cleaner.py +++ b/scripts/tests/unit/test_schema_cleaner.py @@ -156,6 +156,13 @@ def test_field_raises_on_missing_required_attributes(self): with self.assertRaisesRegex(ValueError, "mandatory attributes: {}".format(missing_attribute)): cleaner.field_mandatory_attributes(field) + + def test_field_raises_on_alias_missing_path_attribute(self): + field = self.schema_process()['process']['fields']['pid'] + field['field_details']['type'] = "alias" + with self.assertRaisesRegex(ValueError, + "mandatory attributes: {}".format("path")): + cleaner.field_mandatory_attributes(field) def test_field_simple_cleanup(self): my_field = { From b9657fd6ca83b87b57d6e9b1c061cda871d41b35 Mon Sep 17 00:00:00 2001 From: Richard Gomez Date: Sun, 4 Oct 2020 13:10:28 -0400 Subject: [PATCH 06/11] style: fix linter issues --- scripts/schema/cleaner.py | 2 +- scripts/tests/test_es_template.py | 2 +- scripts/tests/unit/test_schema_cleaner.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/schema/cleaner.py b/scripts/schema/cleaner.py index aebda18ae5..fa5838cbb7 100644 --- a/scripts/schema/cleaner.py +++ b/scripts/schema/cleaner.py @@ -158,7 +158,7 @@ def field_mandatory_attributes(field): return current_field_attributes = sorted(field['field_details'].keys()) missing_attributes = ecs_helpers.list_subtract(FIELD_MANDATORY_ATTRIBUTES, current_field_attributes) - + # The `alias` type requires a target path. # https://github.com/elastic/ecs/issues/876 if field['field_details'].get('type') == 'alias' and 'path' not in current_field_attributes: diff --git a/scripts/tests/test_es_template.py b/scripts/tests/test_es_template.py index ada4e9bba6..0f0fca58df 100644 --- a/scripts/tests/test_es_template.py +++ b/scripts/tests/test_es_template.py @@ -109,7 +109,6 @@ def test_entry_for_index(self): } self.assertEqual(es_template.entry_for(test_map), exp) - def test_entry_for_alias(self): test_map = { 'name': 'test.alias', @@ -133,5 +132,6 @@ def test_entry_for_alias_missing_path(self): "The \[path\] property must be specified for field \[{}\]".format(test_map['name'])): es_template.entry_for(test_map) + if __name__ == '__main__': unittest.main() diff --git a/scripts/tests/unit/test_schema_cleaner.py b/scripts/tests/unit/test_schema_cleaner.py index b159b9ff3d..ba86728e2d 100644 --- a/scripts/tests/unit/test_schema_cleaner.py +++ b/scripts/tests/unit/test_schema_cleaner.py @@ -156,7 +156,7 @@ def test_field_raises_on_missing_required_attributes(self): with self.assertRaisesRegex(ValueError, "mandatory attributes: {}".format(missing_attribute)): cleaner.field_mandatory_attributes(field) - + def test_field_raises_on_alias_missing_path_attribute(self): field = self.schema_process()['process']['fields']['pid'] field['field_details']['type'] = "alias" From 4fba68804468b321e5e12892235134d149ab75c8 Mon Sep 17 00:00:00 2001 From: Richard Gomez <32133502+rgmz@users.noreply.github.com> Date: Tue, 13 Oct 2020 18:53:43 -0400 Subject: [PATCH 07/11] feat(beats): add 'path' to 'allowed_keys' This will automatically include the `path` field in the output, rather than explicitly handling it. As per: https://github.com/elastic/ecs/pull/877/files/b9657fd6ca83b87b57d6e9b1c061cda871d41b35#r504288235 --- scripts/generators/beats.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/scripts/generators/beats.py b/scripts/generators/beats.py index 64d14b5d54..457fecc5ec 100644 --- a/scripts/generators/beats.py +++ b/scripts/generators/beats.py @@ -34,7 +34,7 @@ def fieldset_field_array(source_fields, df_whitelist, fieldset_prefix): allowed_keys = ['name', 'level', 'required', 'type', 'object_type', 'ignore_above', 'multi_fields', 'format', 'input_format', 'output_format', 'output_precision', 'description', - 'example', 'enabled', 'index'] + 'example', 'enabled', 'index', 'path'] multi_fields_allowed_keys = ['name', 'type', 'norms', 'default_field', 'normalizer', 'ignore_above'] fields = [] @@ -62,14 +62,6 @@ def fieldset_field_array(source_fields, df_whitelist, fieldset_prefix): if not ecs_field['flat_name'] in df_whitelist: beats_field['default_field'] = False - # Include the `path` attribute for `alias` types. - # https://github.com/elastic/ecs/issues/876 - if ecs_field['type'] == 'alias': - if 'path' in ecs_field: - beats_field['path'] = ecs_field['path'] - else: - raise ValueError(f'The [path] property must be specified for field [{contextual_name}]') - fields.append(beats_field) return sorted(fields, key=lambda x: x['name']) From 231fe9870a9783c5053bfaa29ad6187a32e201f0 Mon Sep 17 00:00:00 2001 From: Richard Gomez <32133502+rgmz@users.noreply.github.com> Date: Tue, 13 Oct 2020 18:57:12 -0400 Subject: [PATCH 08/11] feat(generator): remove check for 'path' field Given that we're already validating that 'path' exists, this check is redundant. As per: https://github.com/elastic/ecs/pull/877#discussion_r504285680 --- scripts/generators/es_template.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/scripts/generators/es_template.py b/scripts/generators/es_template.py index 3d6c349910..08e925f0ae 100644 --- a/scripts/generators/es_template.py +++ b/scripts/generators/es_template.py @@ -60,10 +60,7 @@ def entry_for(field): elif field['type'] == 'text': ecs_helpers.dict_copy_existing_keys(field, field_entry, ['norms']) elif field['type'] == 'alias': - if 'path' in field: - field_entry['path'] = field['path'] - else: - raise ValueError(f'The [path] property must be specified for field [{field["name"]}]') + ecs_helpers.dict_copy_existing_keys(field, field_entry, ['path']) if 'multi_fields' in field: field_entry['fields'] = {} From 815024d7fb8364eafedde434607a8bb4ad6b5ba2 Mon Sep 17 00:00:00 2001 From: Richard Gomez <32133502+rgmz@users.noreply.github.com> Date: Tue, 13 Oct 2020 19:04:24 -0400 Subject: [PATCH 09/11] docs: added alias changes to CHANGELOG.next.md --- CHANGELOG.next.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.next.md b/CHANGELOG.next.md index 417377de00..a3cbda3ac4 100644 --- a/CHANGELOG.next.md +++ b/CHANGELOG.next.md @@ -45,6 +45,7 @@ Thanks, you're awesome :-) --> * Added check under `--strict` that ensures composite types in example fields are quoted. #966 * Added `ignore_above` and `normalizer` support for keyword multi-fields. #971 * Added `--oss` flag for users who want to generate ECS templates for use on OSS clusters. #991 +* Added the `path` key when type is `alias`, to support the [alias field type](https://www.elastic.co/guide/en/elasticsearch/reference/current/alias.html). #877 #### Improvements From 4f0e378181a52d06bfd7c6bbf00bf681180a1b9f Mon Sep 17 00:00:00 2001 From: Richard Gomez <32133502+rgmz@users.noreply.github.com> Date: Tue, 13 Oct 2020 19:15:06 -0400 Subject: [PATCH 10/11] docs: add alias/path to supported keys --- schemas/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/schemas/README.md b/schemas/README.md index c87be195a3..88440c0354 100644 --- a/schemas/README.md +++ b/schemas/README.md @@ -151,6 +151,18 @@ Supported keys to describe expected values for a field Optionally, entries in this list can specify 'expected\_event\_types'. - expected\_event\_types: list of expected "event.type" values to use in association with that category. + +Supported keys when using the [alias field type](https://www.elastic.co/guide/en/elasticsearch/reference/current/alias.html) + +```YAML + - name: a_field + level: extended + type: alias + path: another_field + description: > + An alias of another field. +``` +- path (optional): The full path to the [aliases' target field](https://www.elastic.co/guide/en/elasticsearch/reference/current/alias.html#alias-targets). #### Multi\_fields From 7e7577afce692818283bb9622f32320b868b6039 Mon Sep 17 00:00:00 2001 From: Richard Gomez <32133502+rgmz@users.noreply.github.com> Date: Tue, 13 Oct 2020 19:20:29 -0400 Subject: [PATCH 11/11] test: remove irrelevant test Throwing an exception during generation was removed in a prior commit, so this test is no longer relevant. --- scripts/tests/test_es_template.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/scripts/tests/test_es_template.py b/scripts/tests/test_es_template.py index 0f0fca58df..9136f8b99e 100644 --- a/scripts/tests/test_es_template.py +++ b/scripts/tests/test_es_template.py @@ -122,16 +122,6 @@ def test_entry_for_alias(self): } self.assertEqual(es_template.entry_for(test_map), exp) - def test_entry_for_alias_missing_path(self): - test_map = { - 'name': 'test.alias', - 'type': 'alias' - } - - with self.assertRaisesRegex(ValueError, - "The \[path\] property must be specified for field \[{}\]".format(test_map['name'])): - es_template.entry_for(test_map) - if __name__ == '__main__': unittest.main()