Skip to content

Billing transform runtime field: Cannot iterate over [java.lang.String] on deployment_tags #91

@JohannesMahne

Description

@JohannesMahne

Summary

The logs-chargeback.billing_cluster_cost transform fails with a Painless script error when ess.billing.deployment_tags is indexed as a single string instead of an array. The runtime mapping for deployment_group uses for (tag in params._source.ess.billing.deployment_tags), which throws when the value is a string.

Step of instructions

Transform setup / runtime phase – the transform runs (or is validated) and fails when querying source indices where deployment_tags is a single string.

Error message

Transform encountered an exception: [Validation Failed: 1: Failed to test query, received status: BAD_REQUEST;]; Will automatically retry [1/-1]
org.elasticsearch.common.ValidationException: Validation Failed: 1: Failed to test query, received status: BAD_REQUEST;
...
Caused by: org.elasticsearch.action.search.SearchPhaseExecutionException: all shards failed
...
Caused by: org.elasticsearch.script.ScriptException: runtime error
Caused by: java.lang.IllegalArgumentException: Cannot iterate over [java.lang.String]

Query / context

The transform’s runtime_mappings.deployment_group.script.source runs over the billing source indices. The failure happens when the transform tests its query (e.g. during start or validation).

Elastic Stack version

9.2.2 (from stack trace)

Steps to reproduce

  1. Ingest ESS Billing (or on-prem billing) data where ess.billing.deployment_tags is sometimes stored as a single string (e.g. one tag) rather than an array.
  2. Install/run the chargeback integration and the billing_cluster_cost transform.
  3. The transform fails with the script error above when it runs or validates the query.

Expected behavior

The transform should run successfully whether deployment_tags is an array of strings or a single string. The runtime script should handle both shapes and still extract chargeback_group:<name> when present.

Root cause

The runtime script assumes params._source.ess.billing.deployment_tags is always iterable (a list). When the source sends or maps deployment_tags as a single string, Painless throws because you cannot use for (tag in ...) on a String.

Fix

Handle both List and String in the Painless script:

if (params._source?.ess?.billing?.deployment_tags != null) {
  def tags = params._source.ess.billing.deployment_tags;
  if (tags instanceof List) {
    for (tag in tags) {
      if (tag.startsWith('chargeback_group:')) {
        emit(tag.substring('chargeback_group:'.length()));
        return;
      }
    }
  } else if (tags instanceof String) {
    if (tags.startsWith('chargeback_group:')) {
      emit(tags.substring('chargeback_group:'.length()));
      return;
    }
  }
}
emit('');

Apply this as the runtime_mappings.deployment_group.script.source in the billing_cluster_cost transform definition (e.g. in the transform YAML that ships with the chargeback integration in the integrations repo: packages/chargeback/elasticsearch/transform/billing_cluster_cost/transform.yml).

References

  • Integrations repo: chargeback transform at packages/chargeback/elasticsearch/transform/billing_cluster_cost/transform.yml
  • Related: deployment group extraction from tags chargeback_group:<group_name>

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions