Skip to content

[Fleet] Add namespace level customization for packages#262568

Merged
jillguyonnet merged 49 commits into
elastic:mainfrom
jillguyonnet:fleet/245181-integration-namespace-custom
May 6, 2026
Merged

[Fleet] Add namespace level customization for packages#262568
jillguyonnet merged 49 commits into
elastic:mainfrom
jillguyonnet:fleet/245181-integration-namespace-custom

Conversation

@jillguyonnet
Copy link
Copy Markdown
Member

@jillguyonnet jillguyonnet commented Apr 10, 2026

Summary

Closes #245181

Fleet currently supports user customization at the type level (e.g. logs@custom) and data-stream level (e.g. logs-system.application@custom), but not at the namespace level. Users managing multiple integrations under a shared namespace must configure each data stream individually.

This PR implements namespace level customization by adding opt-in per-namespace index templates.

This is an API-only solution for now. #264065 will implement UI changes.

Solution rationale

Fleet currently creates one base index template per data stream pattern defined by the integration, e.g. logs-system.application-*. This index templates applies to all matching data streams regardless of namespace, e.g. logs-system.application-production and logs-system.application-staging). This means that namespace-specific component templates (e.g. production@custom and staging@custom) cannot belong to that index template, otherwise they would compete with each other and affect all data streams, not just the relevant namespace.

In this change, Fleet creates a dedicated index template per data stream with a more specific index pattern and higher priority than the base template. This namespace template is a clone of the base template with {namespace}@custom added to composed_of. The base template is never modified.

Example:

Template index_patterns Priority
logs-system.application logs-system.application-* 200
logs-system.application@namespace.production logs-system.application-production* 250

Component templates in logs-system.application:

"composed_of": [
  "logs@mappings",
  "logs@settings",
  "logs-system.application@package",
  "logs@custom",
  "system@custom",
  "logs-system.application@custom",
  "ecs@mappings",
  ".fleet_globals-1",
  ".fleet_agent_id_verification-1"
],

Component templates in logs-system.application@namespace.production:

"composed_of": [
  "logs@mappings",
  "logs@settings",
  "logs-system.application@package",
  "logs@custom",
  "system@custom",
  "production@custom", // namespace level, between package and data stream
  "logs-system.application@custom",
  "ecs@mappings",
  ".fleet_globals-1",
  ".fleet_agent_id_verification-1"
],

Opt-in mechanism

Creating namespace templates for every namespace by default could potentially cause many templates to be created unnecessarily. To mitigate this, namespace level customization is opt-in and managed in the package installation:

{
  "item": {
    "name": "system",
    ...
    "installationInfo": {
      ...
      "namespace_customization_enabled_for": ["production"]
      }
    }
  }
}

Namespaces can be opted in by updating the package installation:

PUT kbn:/api/fleet/epm/packages/system
{
  "namespace_customization_enabled_for": ["production"]
}

Or using the new bulk API:

POST kbn:/api/fleet/epm/packages/_bulk_namespace_customization
{
  "packages": ["system", "nginx", "apache"],
  "enable":  ["production"],
  "disable": ["staging"]
}

Space awareness

Installation saved objects are shared across spaces, meaning the list of opted in namespaces for an installed package is cluster wide: any Kibana space that can see an integration sees the same opt-in list. Index templates and component templates are also cluster wide.

The present implementation attempts to mitigate the write-side of this using the allowed_namespace_prefixes per-space restriction mechanism in Fleet settings (which is already used to gate which namespaces a user can choose for agent/package policies in a given space).

When the user attempts to modify the list of opted in namespaces, it will be validated against allowed namespace prefixes if any (if none are set, there's no validation). For example, if prod namespace is allowed in space A, then from that space prod_eu and prod_us would be allowed, but not qa.

Performance and scaling

Namespace template creation and deletion is handled by an asynchronous task. This addresses risks of latency/timeouts and provides a built-in retry mechanism.

Testing

  1. Per-package opt-in creates namespace templates (existing policies)

    1. Install the System integration with a package policy using namespace production.

    2. Verify no namespace template exists yet:

    GET _index_template/logs-system.application@namespace.production
    

    should return 404.

    1. Opt in production:
    PUT kbn:/api/fleet/epm/packages/system
      {
        "namespace_customization_enabled_for": ["production"]
      }
    
    1. Wait a few seconds for the async task, then verify the namespace index template was created:
    GET _index_template/logs-system.application@namespace.production
    

    In particular, check:

    • index_patterns is ["logs-system.application-production*"]
    • priority is 250
    • composed_of includes production@custom
  2. Namespace template shows in Assets tab

    In Kibana UI → Integrations → System → Assets tab, confirm logs-system.application@namespace.production (and any other per-dataset variants) appear alongside other Fleet-managed assets.

  3. Namespace component templates are applied to their specific data streams only

    1. Create a production@customcomponent template, e.g.:
    PUT _component_template/production@custom
    {
      "template": {
        "settings": {
          "index.number_of_replicas": 2
        }
      }
    }
    
    1. Trigger a rollover if you already have data, or ingest a document to create backing indices, e.g.
    POST logs-system.application-production/_doc
    {
      "@timestamp": "2026-04-15T00:00:00Z",
      "message": "test document"
    }
    

    This should create the data stream and its first backing index automatically, using the logs-system.application@namespace.production index template (priority 250) rather than the base logs-system.application template (priority 200).
    3. Verify the production namespace data stream has number_of_replicas: 2:

    GET logs-system.application-production/_settings/index.number_of_replicas
    
    1. Verify a data stream in a different namespace does not have number_of_replicas: 2, confirming namespace isolation.
  4. Opt-out deletes index templates but keeps component templates

    1. Opt out production:
    PUT kbn:/api/fleet/epm/packages/system
      {
        "namespace_customization_enabled_for": []
      }
    
    1. Wait a few seconds for the async task, then verify the namespace index templates were deleted but the production@customcomponent template still exists.
  5. Uninstall cleans up namespace templates

    1. Opt production back in, namespace index template should exist.
    2. Delete all System policies, uninstall integration and verify the namespace index templates were deleted.
  6. Bulk endpoint

    1. Install System and Nginx and enable production and staging namespaces for both:
    POST kbn:/api/fleet/epm/packages/_bulk_namespace_customization
    {
      "packages": ["system", "nginx"],
      "enable": ["production", "staging"]
    }
    
    1. The response should be:
    {
      "items": [
        {
          "name": "system",
          "success": true,
          "namespace_customization_enabled_for": [
            "production"
          ]
        },
        {
          "name": "nginx",
          "success": true,
          "namespace_customization_enabled_for": [
            "production"
          ]
        }
      ]
    }
    
    1. Test combinations of enable and disable.
    2. Verify that passing the name of a non installed package results in error:
    {
      "items": [
        {
          "name": "system",
          "success": true,
          "namespace_customization_enabled_for": [
            "production"
          ]
        },
        {
          "name": "apache",
          "success": false,
          "error": "Package apache is not installed"
        }
      ]
    }
    
    1. Verify that it is not allowed to pass the same namespace in enable and disable:
    {
      "statusCode": 400,
      "error": "Bad Request",
      "message": "Namespaces must not appear in both enable and disable: production"
    }
    
  7. Space awareness

    1. Create a custom space and go to it.
    2. Configure allowed namespace prefixes in that space:
    PUT kbn:/api/fleet/space_settings
    { 
      "allowed_namespace_prefixes": ["prod"] 
    }
    
    1. Verify that you now can't opt in a namespace that doesn't match the allowed prefix:
    PUT kbn:/s/team-a/api/fleet/epm/packages/system
    { 
      "namespace_customization_enabled_for": ["staging"] 
    }
    

    Should result in:

    {
      "statusCode": 400,
      "error": "Bad Request",
      "message": "Cannot change namespace customization for: staging. Allowed prefixes in this space: prod"
    }
    
    1. Repeat with a matching namespace (e.g. prod_eu), it should work.
    2. Now change the allowed namespace prefixes to staging and try to opt out prod_eu (pass an empty array): it should reject with Cannot change namespace customization for: prod_eu. Allowed prefixes in this space: staging.
    3. Do similar checks with the bulk endpoint: note that if you pass multiple namespaces to opt in/out and at least one of them fails for a given package, the update will fail for this package.

Checklist

Check the PR satisfies following conditions.

Reviewers should verify this PR satisfies this list as well.

  • Any text added follows EUI's writing guidelines, uses sentence case text and includes i18n support
  • Documentation was added for features that require explanation or tutorials
  • Unit or functional tests were updated or added to match the most common scenarios
  • If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the docker list
  • This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The release_note:breaking label should be applied in these situations.
  • Flaky Test Runner was used on any tests changed
  • The PR description includes the appropriate Release Notes section, and the correct release_note:* label is applied per the guidelines
  • Review the backport guidelines and apply applicable backport:* labels.

Identify risks

Risk of eventual consistency gap if package installation SO is saved (synchronous) but the async task fails to create/delete index templates.

Release note

Add opt-in namespace level customization to integrations.

@jillguyonnet jillguyonnet self-assigned this Apr 10, 2026
@jillguyonnet jillguyonnet added release_note:enhancement backport:skip This PR does not require backporting Team:Fleet Team label for Observability Data Collection Fleet team labels Apr 10, 2026
@jillguyonnet jillguyonnet force-pushed the fleet/245181-integration-namespace-custom branch from 83dbcb0 to 5047d41 Compare April 13, 2026 13:45
@jillguyonnet jillguyonnet force-pushed the fleet/245181-integration-namespace-custom branch from 5047d41 to a8be4ae Compare April 15, 2026 12:01
@jillguyonnet jillguyonnet changed the title [Fleet] Add <namespace>@custom component templates to package index templates [Fleet] Add namespace level customization for packages Apr 15, 2026
@jillguyonnet jillguyonnet marked this pull request as ready for review April 16, 2026 08:40
@jillguyonnet jillguyonnet requested review from a team as code owners April 16, 2026 08:40
@elasticmachine
Copy link
Copy Markdown
Contributor

Pinging @elastic/fleet (Team:Fleet)

@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp Bot commented Apr 16, 2026

Catch flakiness early (recommended)

Recommended before merge: run the flaky test runner against this PR to catch flakiness early.

Trigger a run with the Flaky Test Runner UI or post this comment on the PR:

/flaky ftrConfig:x-pack/platform/test/fleet_api_integration/config.space_awareness.ts:30

This check is experimental. Share your feedback in the #appex-qa channel.

Posted via Macroscope — Flaky Test Runner nudge

@jillguyonnet
Copy link
Copy Markdown
Member Author

/flaky ftrConfig:x-pack/platform/test/fleet_api_integration/config.space_awareness.ts:30

@kibanamachine
Copy link
Copy Markdown
Contributor

Flaky Test Runner

✅ Build triggered - kibana-flaky-test-suite-runner#11659

  • x-pack/platform/test/fleet_api_integration/config.space_awareness.ts x30

@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp Bot commented Apr 16, 2026

Approvability

Verdict: Needs human review

This PR introduces a new feature for namespace-level package customization, including new API endpoints, background tasks, and schema changes. Multiple unresolved review comments raise concerns about data integrity, performance, and error handling. The changes affect code owned by @elastic/fleet and require review by designated owners.

You can customize Macroscope's approvability policy. Learn more.

@jillguyonnet jillguyonnet requested a review from jsoriano April 16, 2026 08:43
@kibanamachine
Copy link
Copy Markdown
Contributor

Flaky Test Runner Stats

🎉 All tests passed! - kibana-flaky-test-suite-runner#11659

[✅] x-pack/platform/test/fleet_api_integration/config.space_awareness.ts: 30/30 tests passed.

see run history

@jillguyonnet
Copy link
Copy Markdown
Member Author

@elasticmachine merge upstream

@jillguyonnet
Copy link
Copy Markdown
Member Author

jillguyonnet commented May 1, 2026

Hi @lucabelluccini Thanks a lot for your input, it will be super helpful for scoping followup work.

namespace is necessarily not mapped a Kibana space.

Your interpretation is correct:

  • Namespace lives on the cluster-wide Installation SO. Namespaces are not partitioned by Kibana space.
  • By default, users can pick any namespace name
  • If the optional allowed_namespace_prefixes per-space setting is used, then it acts as a restriction for this space

Consider a user might have already done customizations before the introduction of this feature

Very good point. I can see three scenarios:

1. User cloned the base index template (what is described in https://www.elastic.co/docs/reference/fleet/data-streams-scenario3): this would likely imply two index templates with overlapping index patterns and the same priority, e.g.:

  • Cloned template
    • Name the user picked, e.g. logs-nginx.access-production
    • index_patterns: ["logs-nginx.access-production*"] (note the wildcard)
    • Priority: usually 250
  • Managed template

In that case, ES should reject creating the second index template, so the actual outcome would be that the user's cloned index template stays and Fleet's attempt at creating the managed one fails.

In the case that they set a different priority, both templates will coexist and the higher-priority one wins for matching data streams.

2. User only created <namespace>@custom component template. That scenario seems fine as long as they're aware of the feature since this is the component template we expect them to create (I think this is what you mean by "I think this is covered in 99% of the case"?

3. User edited the original Fleet-managed index template directly: those customizations would get overridden if they enable namespace customization.

In any case, it is clear that we need solid documentation to help them with the migration.

What we could do to help:

  • Find the resolved index template with POST /_index_template/_simulate_index/<index-name> before we attempt to create a namespace index template for a given data stream and check whether the resolved template is the base template or something else (a clone). That would at least allow us to log a meaningful warning.
  • Apply UX to this. More involved, needs discussion.

I would be happy to add the first addition to this PR if it seems useful, as it should be fairly small. I think the second one, if applicable, needs to be fleshed out and be its own followup.
Edit: I filed #267291 to tackle this.

Implementing this in Fleet is a great step forward, but not having it for any "managed" index template is a missed opportunity

Fair point, it would be great if namespace-level customization could eventually fill that gap. This is broader than Fleet, since APM's templates are no longer installed by EPM. We'd probably need either the stack-managed template owners to extend their composed_of with <namespace>@custom, or a platform-level mechanism.

@kibanamachine
Copy link
Copy Markdown
Contributor

💚 Build Succeeded

Metrics [docs]

Public APIs missing comments

Total count of every public API that lacks a comment. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats comments for more detailed information.

id before after diff
fleet 1727 1729 +2

Page load bundle

Size of the bundles that are downloaded on every page load. Target size is below 100kb

id before after diff
fleet 204.2KB 204.3KB +74.0B
Unknown metric groups

API count

id before after diff
fleet 1935 1938 +3

History

cc @jillguyonnet

@criamico criamico self-assigned this May 4, 2026
@criamico
Copy link
Copy Markdown
Member

criamico commented May 5, 2026

@elasticmachine merge upstream

@criamico criamico requested a review from jeramysoucy May 5, 2026 07:43
Copy link
Copy Markdown
Member

@vishaangelova vishaangelova left a comment

Choose a reason for hiding this comment

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

A couple of small suggestions, otherwise LGTM!

Comment thread x-pack/platform/plugins/shared/fleet/server/types/rest_spec/epm.ts Outdated
criamico and others added 3 commits May 5, 2026 10:53
Co-authored-by: Visha Angelova <91186315+vishaangelova@users.noreply.github.com>
Co-authored-by: Visha Angelova <91186315+vishaangelova@users.noreply.github.com>
@jeramysoucy jeramysoucy removed their request for review May 5, 2026 09:46
@criamico criamico requested a review from darnautov May 5, 2026 09:53
Comment thread x-pack/platform/plugins/shared/fleet/server/types/rest_spec/epm.ts Fixed
@criamico
Copy link
Copy Markdown
Member

criamico commented May 6, 2026

@elasticmachine merge upstream

@kibanamachine
Copy link
Copy Markdown
Contributor

💛 Build succeeded, but was flaky

Failed CI Steps

Test Failures

  • [job] [logs] Jest Integration Tests #8 / step level timeout should have execution duration close to configured timeout value

Metrics [docs]

Public APIs missing comments

Total count of every public API that lacks a comment. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats comments for more detailed information.

id before after diff
fleet 1727 1729 +2

Page load bundle

Size of the bundles that are downloaded on every page load. Target size is below 100kb

id before after diff
fleet 203.9KB 204.0KB +74.0B
Unknown metric groups

API count

id before after diff
fleet 1935 1938 +3

History

cc @criamico @jillguyonnet

@jillguyonnet jillguyonnet merged commit c6253db into elastic:main May 6, 2026
32 checks passed
@jillguyonnet jillguyonnet deleted the fleet/245181-integration-namespace-custom branch May 11, 2026 08:25
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 v9.5.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Fleet] Add support for customizing integration datastream at the namespace@custom level of granularity