Skip to content

[Fleet] Add support for Runtime Fields#161129

Merged
mrodm merged 17 commits intoelastic:mainfrom
mrodm:add_runtime_fields_support
Jul 12, 2023
Merged

[Fleet] Add support for Runtime Fields#161129
mrodm merged 17 commits intoelastic:mainfrom
mrodm:add_runtime_fields_support

Conversation

@mrodm
Copy link
Copy Markdown
Contributor

@mrodm mrodm commented Jul 3, 2023

Summary

Closes #155255
Closes elastic/package-spec#39

Add support in Fleet for Runtime fields, based on these docs:

Given these field definitions in packages:

- name: bar
  type: boolean
- name: uptime
  type: keyword
- name: runtime_boolean
  type: boolean
  runtime: true
- name: runtime.day
  type: keyword
  runtime: >-
    emit(doc['@timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL, Locale.ROOT))
- name: to_be_long
  type: long
  runtime: true
- name: runtime.date
  type: date
  date_format: 'yyyy-MM-dd'
  runtime: >-
    emit(doc['@timestamp'].value.toEpochMilli())
- name: runtime.epoch_milli
  type: long
  runtime: >-
    emit(doc['@timestamp'].value.toEpochMilli())
- name: lowercase
  type: keyword
  runtime: >-
    emit(doc['uppercase'].value.toLowerCase())
- name: labels.*
  type: long
  object_type_mapping_type: double
  runtime: true
- name: responses
  type: group
  fields:
    - name: runtime_group_boolean
      type: boolean
      runtime: true
    - name: foo
      type: boolean

and this definition in the manifest

elasticsearch:
  index_template:
    mappings:
      runtime:
        day_of_week_two:
          type: keyword
          script:
            source: "emit(doc['@timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL, Locale.ROOT))"

This PR adds the required fields into the mappings key when installing the package. For this example, the resulting mappings are (just showing the relevant data for these changes):

{
  ".ds-logs-runtime_fields.foo-default-2023.07.10-000001": {
    "mappings": {
      "dynamic_templates": [
        {
          "labels.*": {
            "path_match": "labels.*",
            "match_mapping_type": "double",
            "runtime": {
              "type": "long"
            }
          }
        }
      ],
      "runtime": {
        "day_of_week_two": {
          "type": "keyword",
          "script": {
            "source": "emit(doc['@timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL, Locale.ROOT))",
            "lang": "painless"
          }
        },
        "labels.a": {
          "type": "long"
        },
        "labels.b": {
          "type": "long"
        },
        "lowercase": {
          "type": "keyword",
          "script": {
            "source": "emit(doc['uppercase'].value.toLowerCase())",
            "lang": "painless"
          }
        },
        "responses.runtime_group_boolean": {
          "type": "boolean"
        },
        "runtime.date": {
          "type": "date",
          "script": {
            "source": "emit(doc['@timestamp'].value.toEpochMilli())",
            "lang": "painless"
          },
          "format": "yyyy-MM-dd"
        },
        "runtime.day": {
          "type": "keyword",
          "script": {
            "source": "emit(doc['@timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL, Locale.ROOT))",
            "lang": "painless"
          }
        },
        "runtime.epoch_milli": {
          "type": "long",
          "script": {
            "source": "emit(doc['@timestamp'].value.toEpochMilli())",
            "lang": "painless"
          }
        },
        "runtime_boolean": {
          "type": "boolean"
        },
        "to_be_long": {
          "type": "long"
        }
      },
      "properties": {
        "@timestamp": {
          "type": "date",
          "ignore_malformed": false
        },
        "bar": {
          "type": "boolean"
        },
        "data_stream": {
          "properties": {
            "dataset": {
              "type": "constant_keyword"
            },
            "namespace": {
              "type": "constant_keyword"
            },
            "type": {
              "type": "constant_keyword"
            }
          }
        },
        "labels": {
          "type": "object"
        },
        "message": {
          "type": "keyword",
          "ignore_above": 1024
        },
        "responses": {
          "properties": {
            "foo": {
              "type": "boolean"
            }
          }
        },
        "uppercase": {
          "type": "keyword",
          "ignore_above": 1024
        },
        "user": {
          "properties": {
            "id": {
              "type": "keyword",
              "ignore_above": 1024
            }
          }
        }
      }
    }
  }
}

Tested manually installing a package containing runtime field definitions as the example above.

Tested also indexing some documents and retrieving the runtime fields:

  • Indexing documents:
POST /logs-runtime_fields.foo-default/_doc/
{
  "@timestamp": "2023-07-07T13:32:09.000Z",
  "datastream": {
    "dataset": "logs-runtime_fields.foo",
    "namespace": "default",
    "type": "logs"
  },
  "user": {
    "id": "8a4f500d"
  },
  "message": "Login successful",
  "labels": {
    "a": 1.6,
    "b": 2.5
  },
  "uppercase": "SOMETHING",
  "to_be_long": 1.6,
  "runtime_boolean": true,
  "responses.runtime_group_boolean": false
}
  • Retrieving runtime fields (_source disabled):
GET logs-runtime_fields.foo-default/_search
{
  "fields": [
    "@timestamp",
    "runtime_boolean",
    "responses.runtime_group_boolean",
    "runtime.day",
    "runtime.date",
    "runtime.epoch_milli",
    "labels.*",
    "uppercase",
    "lowercase",
    "to_be_long"
  ],
  "_source": false
}
  • Output:
...
    "hits": [
      {
        "_index": ".ds-logs-runtime_fields.foo-default-2023.07.10-000001",
        "_id": "_7p1P4kBtEvrlGnsxiFN",
        "_score": 1,
        "fields": {
          "uppercase": [
            "SOMETHING"
          ],
          "runtime.date": [
            "2023-07-10"
          ],
          "@timestamp": [
            "2023-07-10T09:33:09.000Z"
          ],
          "lowercase": [
            "something"
          ],
          "to_be_long": [
            1
          ],
          "runtime_boolean": [
            true
          ],
          "runtime.day": [
            "Monday"
          ],
          "labels.a": [
            1
          ],
          "labels.b": [
            2
          ],
          "responses.runtime_group_boolean": [
            false
          ],
          "runtime.epoch_milli": [
            1688981589000
          ]
        }
      }
    ]
...

Checklist

Delete any items that are not applicable to this PR.

@mrodm mrodm self-assigned this Jul 3, 2023
@ghost
Copy link
Copy Markdown

ghost commented Jul 3, 2023

🤖 GitHub comments

Expand to view the GitHub comments

Just comment with:

  • /oblt-deploy : Deploy a Kibana instance using the Observability test environments.
  • run elasticsearch-ci/docs : Re-trigger the docs validation. (use unformatted text in the comment!)

@mrodm
Copy link
Copy Markdown
Contributor Author

mrodm commented Jul 10, 2023

@elasticmachine merge upstream

runtimeProperties: fieldProps,
});
}
return;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading the docs, it looks like these runtime fields should not be added to the properties section. That's why it has been added here this return.

Comment on lines +204 to +223
const pathMatch = path.includes('*') ? path : `${path}.*`;

let dynProperties: Properties = getDefaultProperties(field);
let matchingType: string | undefined;
switch (field.object_type) {
case 'keyword':
dynProperties.type = field.object_type;
matchingType = field.object_type_mapping_type ?? 'string';
break;
case 'double':
case 'long':
case 'boolean':
dynProperties = {
type: field.object_type,
time_series_metric: field.metric_type,
};
matchingType = field.object_type_mapping_type ?? field.object_type;
default:
break;
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to what it is done in this block:

switch (field.object_type) {
case 'histogram':
dynProperties = histogram(field);
matchingType = field.object_type_mapping_type ?? '*';
break;
case 'text':
dynProperties.type = field.object_type;
matchingType = field.object_type_mapping_type ?? 'string';
break;
case 'keyword':
dynProperties.type = field.object_type;
matchingType = field.object_type_mapping_type ?? 'string';
break;
case 'byte':
case 'double':
case 'float':
case 'long':
case 'short':
case 'boolean':
dynProperties = {
type: field.object_type,
time_series_metric: field.metric_type,
};
matchingType = field.object_type_mapping_type ?? field.object_type;
default:
break;

but as runtime fields support fewer types, there were removed the ones not supported

https://www.elastic.co/guide/en/elasticsearch/reference/8.8/runtime-mapping-fields.html#runtime-mapping-fields

Comment on lines +230 to +236
ctx.addDynamicMapping({
path,
pathMatch,
matchingType,
properties: dynProperties,
runtimeProperties: fieldProps,
});
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +138 to +142
if (dynamicMapping.runtimeProperties !== undefined) {
dynamicTemplate.runtime = dynamicMapping.runtimeProperties;
} else {
dynamicTemplate.mapping = dynamicMapping.properties;
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a dynamic template, mapping and runtime cannot be defined at the same time in the object.

@kibana-ci
Copy link
Copy Markdown

💚 Build Succeeded

Metrics [docs]

Unknown metric groups

ESLint disabled line counts

id before after diff
enterpriseSearch 14 16 +2
securitySolution 408 412 +4
total +6

Total ESLint disabled count

id before after diff
enterpriseSearch 15 17 +2
securitySolution 487 491 +4
total +6

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

cc @mrodm

@mrodm mrodm changed the title [Fleet] Add support for runtimeFields [Fleet] Add support for Runtime Fields Jul 10, 2023
@mrodm mrodm added release_note:enhancement Team:Fleet Team label for Observability Data Collection Fleet team v8.10.0 backport:skip This PR does not require backporting labels Jul 10, 2023
@mrodm mrodm marked this pull request as ready for review July 10, 2023 16:19
@mrodm mrodm requested a review from a team as a code owner July 10, 2023 16:19
@elasticmachine
Copy link
Copy Markdown
Contributor

Pinging @elastic/fleet (Team:Fleet)


if (!('settings' in packageTemplate)) {
throw new Error('no mappings on package template');
throw new Error('no settings on package template');
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice finding 🔍

Copy link
Copy Markdown
Member

@nchaulet nchaulet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚀

Copy link
Copy Markdown
Member

@criamico criamico left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚢

@mrodm mrodm merged commit 9a7cc5a into elastic:main Jul 12, 2023
@mrodm mrodm deleted the add_runtime_fields_support branch July 12, 2023 07:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:skip This PR does not require backporting release_note:enhancement Team:Fleet Team label for Observability Data Collection Fleet team v8.10.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Fleet] Add support for runtime fields in packages Add support for runtime fields in packages

6 participants