Skip to content

azure: add routing system test and fake EventHub server - #20581

Open
efd6 wants to merge 1 commit into
elastic:mainfrom
efd6:20459-azure
Open

azure: add routing system test and fake EventHub server#20581
efd6 wants to merge 1 commit into
elastic:mainfrom
efd6:20459-azure

Conversation

@efd6

@efd6 efd6 commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Proposed commit message

azure: add routing system test and fake EventHub server

Add a script-based system test that verifies each Azure log category
routes to the correct data stream. The test starts a fake EventHub
server that speaks AMQP 1.0 on port 5672 and serves Azure Blob
Storage REST on port 10000, delivers one event per Azure log
category, then checks that each event lands in the expected index.

The fake server (_dev/scripts/azure-eh/main.go) handles the full
AMQP 1.0 handshake including CBS token authentication, link flow
control, and message transfer. Messages include the annotations
x-opt-enqueued-time, x-opt-sequence-number, and x-opt-offset that
the azeventhubs receiver expects. The blob storage implementation
generates correct XML for list-blobs responses, including Last-Modified
and Etag in <Properties> and per-key child elements in <Metadata>,
which the checkpoint store relies on to track partition ownership.

Add a storage_account_connection_string package variable so the test
can point the checkpoint store at the fake server's blob endpoint.
When set, it is used as-is; the connection string built from
storage_account and storage_account_key is used otherwise.

Checklist

  • I have reviewed tips for building integrations and this pull request is aligned with them.
  • I have verified that all data streams collect metrics or logs.
  • I have added an entry to my package's changelog.yml file.
  • I have verified that Kibana version constraints are current according to guidelines.
  • I have verified that any added dashboard complies with Kibana's Dashboard good practices

Author's Checklist

  • [ ]

How to test this PR locally

Related issues

Screenshots

@efd6 efd6 self-assigned this Aug 6, 2026
@efd6 efd6 added enhancement New feature or request Integration:azure Azure Logs Team:Security-Service Integrations Security Service Integrations team [elastic/security-service-integrations] labels Aug 6, 2026
Add a script-based system test that verifies each Azure log category
routes to the correct data stream. The test starts a fake EventHub
server that speaks AMQP 1.0 on port 5672 and serves Azure Blob
Storage REST on port 10000, delivers one event per Azure log
category, then checks that each event lands in the expected index.

The fake server (_dev/scripts/azure-eh/main.go) handles the full
AMQP 1.0 handshake including CBS token authentication, link flow
control, and message transfer. Messages include the annotations
x-opt-enqueued-time, x-opt-sequence-number, and x-opt-offset that
the azeventhubs receiver expects. The blob storage implementation
generates correct XML for list-blobs responses, including Last-Modified
and Etag in <Properties> and per-key child elements in <Metadata>,
which the checkpoint store relies on to track partition ownership.

Add a storage_account_connection_string package variable so the test
can point the checkpoint store at the fake server's blob endpoint.
When set, it is used as-is; the connection string built from
storage_account and storage_account_key is used otherwise.
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

✅ Elastic Docs Style Checker (Vale)

No issues found on modified lines!


The Vale linter checks documentation changes against the Elastic Docs style guide. To use Vale locally or report issues, refer to Elastic style guide for Vale.

@elastic-vault-github-plugin-prod

Copy link
Copy Markdown
Contributor

✅ All changelog entries have the correct PR link.

@elastic-vault-github-plugin-prod

Copy link
Copy Markdown
Contributor

🚀 Benchmarks report

To see the full report comment with /test benchmark fullreport

@infra-vault-gh-plugin-prod

Copy link
Copy Markdown

💚 Build Succeeded

cc @efd6

@efd6
efd6 marked this pull request as ready for review August 10, 2026 00:48
@efd6
efd6 requested review from a team as code owners August 10, 2026 00:48
@infra-vault-gh-plugin-prod

Copy link
Copy Markdown

Pinging @elastic/security-service-integrations (Team:Security-Service Integrations)

required: false
show_user: false
description: >-
(Optional) A full connection string for the Storage Account, including the endpoint. When set, this overrides the connection string built from **Storage Account** and **Storage Account Key**.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: 🟡 Medium confidence: high path: packages/azure/manifest.yml:89

This package-level variable is only read by the events data stream, so the description promising it overrides the built connection string is wrong for the other 13 azure-eventhub streams. Scope the description to the v2 events stream (or add the same override to the other templates).

Details

Vars declared at the package root apply to every policy template in the package, so this option is settable on adlogs, activitylogs, platformlogs, firewall_logs, eventhub, etc. But only data_stream/events/agent/stream/stream.yml.hbs consults it. Every other stream template (data_stream/*/agent/stream/azure-eventhub.yml.hbs and data_stream/eventhub/agent/stream/stream.yml.hbs, all at line 42) still unconditionally emits storage_account_connection_string: DefaultEndpointsProtocol=https;AccountName={{storage_account}};AccountKey={{storage_account_key}};EndpointSuffix={{endpoint_suffix}}. Setting the new var on any of those is therefore a silent no-op, while the description states it "overrides the connection string built from Storage Account and Storage Account Key". Separately, the closing sentence on the next line calls the target "a custom S3-compatible store" — the input speaks the Azure Blob Storage REST API, not S3, so that example is misleading.

Recommendation:

Either scope the wording to the data stream that honours it, or extend the other azure-eventhub.yml.hbs templates with the same {{#if}}/{{else}} block. The cheap fix:

  - name: storage_account_connection_string
    type: password
    secret: true
    title: Storage Account Connection String
    multi: false
    required: false
    show_user: false
    description: >-
      (Optional, Azure Logs (v2 preview) only) A full connection string for the Storage Account, including the endpoint.
      When set, it replaces the connection string built from **Storage Account** and **Storage Account Key** for the `events` data stream;
      the v1 data streams ignore it. Use this to point at a non-standard blob endpoint, for example a local Azurite emulator.

🤖 AI-Generated Review | Vera Review Bot | 📚 Knowledge base: integration-skills

⚠️ Automated review — verify suggestions before applying.

// since copyOwnershipPropsFromBlob dereferences both without nil checks.
var buf strings.Builder
buf.WriteString(`<?xml version="1.0" encoding="utf-8"?><EnumerationResults><Blobs>`)
for name, item := range blobs {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: 🟡 Medium confidence: high path: packages/azure/_dev/scripts/azure-eh/main.go:157

listBlobs ranges over the container map after dropping the mutex while putBlob inserts into that same map under it, which Go aborts with an uncatchable fatal error. Hold the lock across the whole iteration.

Details

Lines 147-149 take s.mu only long enough to fetch the inner map, then this loop iterates it with the lock released; putBlob (line 205) writes s.containers[container][blob] = ... while holding the lock. The Azure blob checkpoint store used by the v2 processor lists ownership blobs from its load-balancer goroutine while partition clients create the ownership/checkpoint blobs from another goroutine, so the two overlap during startup. A concurrent map read and map write is a Go runtime fatal error that recover cannot catch: the fake server dies and the routing assertions fail — intermittently, which is the worst failure mode for CI. getBlob (lines 251-262) has the same shape, reading item.metadata and item.data after unlocking while setMetadata (lines 232-237) reassigns those fields under the lock; that one is a plain data race rather than a fatal abort, but it can also serve a torn view of a checkpoint blob.

Recommendation:

Keep the mutex for the whole read in both methods — the fake server does no slow work while holding it:

func (s *blobStore) listBlobs(w http.ResponseWriter, r *http.Request, container string) {
	prefix := r.URL.Query().Get("prefix")
	s.mu.Lock()
	defer s.mu.Unlock()
	blobs := s.containers[container]
	// ... unchanged ...
}

func (s *blobStore) getBlob(w http.ResponseWriter, container, blob string) {
	s.mu.Lock()
	defer s.mu.Unlock()
	blobs := s.containers[container]
	var item *blobItem
	if blobs != nil {
		item = blobs[blob]
	}
	// ... unchanged ...
}

🤖 AI-Generated Review | Vera Review Bot | 📚 Knowledge base: integration-skills

⚠️ Automated review — verify suggestions before applying.

{{/if}}
{{#if storage_account_connection_string}}
storage_account_connection_string: {{storage_account_connection_string}}
{{else}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: 🔵 Low confidence: medium path: packages/azure/data_stream/events/agent/stream/stream.yml.hbs:42

Putting storage_account_key inside the {{else}} branch means setting the new override also drops the key, which processor v1 requires — emit the key independently of the connection-string override.

Details

In filebeat's azure-eventhub input, validateStorageAccountConfig returns no storage account key configured (config: storage_account_key) when processor_version: v1 and storage_account_key is empty; only v2 accepts a bare storage_account_connection_string (and warns that the key is unused). processor_version is still a selectable var on this data stream (default v2, with a v1 (legacy) option), so a policy that sets storage_account_connection_string while pinned to v1 now renders a config the input rejects at startup, where before the change the key was always emitted alongside the built connection string. Both vars are show_user: false, so the blast radius is small — hence low severity — but the regression is in the rendered template as written.

Recommendation:

Emit the key on its own and let the override apply only to the connection string:

{{#if storage_account_key}}
storage_account_key: {{storage_account_key}}
{{/if}}
{{#if storage_account_connection_string}}
storage_account_connection_string: {{storage_account_connection_string}}
{{else}}
{{#if storage_account_key}}
storage_account_connection_string: DefaultEndpointsProtocol=https;AccountName={{storage_account}};AccountKey={{storage_account_key}};EndpointSuffix={{endpoint_suffix}}
{{/if}}
{{/if}}

If you would rather not emit the key for v2 policies (filebeat logs a deprecation warning when it sees it), gate the first block on {{#contains "v1" processor_version}} instead.


🤖 AI-Generated Review | Vera Review Bot | 📚 Knowledge base: integration-skills

⚠️ Automated review — verify suggestions before applying.

@vera-review-bot

Copy link
Copy Markdown

Review summary

Issues found across the latest commits 247d00a — 2 medium, 1 low
  • 🟡 This package-level variable is only read by the events data stream, so the description promising it overrides the built connection string is wrong for the other 13 azure-eventhub streams. Scope the description to the v2 events stream (or add the same override to the other templates). (link) (Unresolved)
  • 🟡 listBlobs ranges over the container map after dropping the mutex while putBlob inserts into that same map under it, which Go aborts with an uncatchable fatal error. Hold the lock across the whole iteration. (link) (Unresolved)
  • 🔵 Putting storage_account_key inside the {{else}} branch means setting the new override also drops the key, which processor v1 requires — emit the key independently of the connection-string override. (link) (Unresolved)

A new commit triggers another review — at most once every 15 minutes. I skip the PR while it's approved or has merge conflicts.

🤖 AI-Generated Review | Vera Review Bot | 📚 Knowledge base: integration-skills

⚠️ Automated review — verify suggestions before applying.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request Integration:azure Azure Logs Team:Security-Service Integrations Security Service Integrations team [elastic/security-service-integrations]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

azure: add script test for events routing rules

1 participant