Repository: elastic/ecs

Issue:
Add generator support for alias datatype
<!--
Please first search existing issues for the changes you are requesting; it may already exist as an open issue.

Please fill in the following sections describing your proposed changes: -->

**Summary**

Presently, the generator does not include the `path` field in the generated template, meaning that it does not properly support the [alias datatype](https://www.elastic.co/guide/en/elasticsearch/reference/master/alias.html).

**Motivation**:

When migrating to ECS the generator provides a powerful way to generate templates, however, in certain cases it's desirable to be able to create a schema of aliases for deprecated fields to provide backwards-compatibility.

**Detailed Design**:
N/A



Discussion:

Resolving patch:
diff --git a/scripts/generators/beats.py b/scripts/generators/beats.py
--- 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 = []
diff --git a/scripts/generators/es_template.py b/scripts/generators/es_template.py
--- a/scripts/generators/es_template.py
+++ b/scripts/generators/es_template.py
@@ -59,6 +59,8 @@ 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':
+            ecs_helpers.dict_copy_existing_keys(field, field_entry, ['path'])
 
         if 'multi_fields' in field:
             field_entry['fields'] = {}
diff --git a/scripts/schema/cleaner.py b/scripts/schema/cleaner.py
--- 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://github.com/elastic/ecs/issues/876
+    if field['field_details'].get('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),


Test patch:
diff --git a/scripts/tests/test_es_template.py b/scripts/tests/test_es_template.py
--- a/scripts/tests/test_es_template.py
+++ b/scripts/tests/test_es_template.py
@@ -109,6 +109,19 @@ 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)
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/scripts/tests/unit/test_schema_cleaner.py b/scripts/tests/unit/test_schema_cleaner.py
--- a/scripts/tests/unit/test_schema_cleaner.py
+++ b/scripts/tests/unit/test_schema_cleaner.py
@@ -157,6 +157,13 @@ def test_field_raises_on_missing_required_attributes(self):
                                         "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 = {
             'field_details': {