fluent-plugin-kusto 1.1.2 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab5bb4131b0e2b0939db74f864dcd1ecad5ad2ca48528149f173f9c2aafff12a
4
- data.tar.gz: 5d6888cc12052c1e9422c22846a5f20a13169e7fd3f43c6a84b11508b10a4df5
3
+ metadata.gz: 3190173672cb7fccc00ecf36ff4db6de603cb53a0f25d2f1633d267a072bd9df
4
+ data.tar.gz: 259a7c0b39e550380bbaddd680c5121ea432d72c8f1588a8b16d6ee9c8a3b1e4
5
5
  SHA512:
6
- metadata.gz: 2572e784885fce4b64278ebd7d5bf6e6f9738396dbf0f101c0870acf6c3389119cd6b19aedcbd5d1a52f78ddda211a7f659b21b5e492f79a971b59d96b561861
7
- data.tar.gz: f4dc859e2bb06f43a3e9d398f85b6a6ea7d790f6d68d2d8b9fef1a316604f3eef9736132dc5d5851c0e3b54ece3d6f60194dbee0118e14f95cce5280b0c43834
6
+ metadata.gz: '09aca848eece3be5f0bf58ae6aa0fbede9d1a57b7f0227a50a5a0d102b941fdc81cab7410cbf0d05a232eda7ba2b75fc84d9b0d7b7f5c55909f9a6f715741cd2'
7
+ data.tar.gz: adff070c6f8d48665d17bb336e1fa04b2d500ed1f22f13917b66c4d148e2f5f7810016affede57800ddc2692c0f0b46af719cbdd4f8073ea85f8728cbc002b72
data/README.md CHANGED
@@ -50,7 +50,7 @@ $ gem install fluent-plugin-kusto
50
50
  Add the following line to your Gemfile:
51
51
 
52
52
  ```ruby
53
- gem "fluent-plugin-kusto", "~> 1.1.2"
53
+ gem "fluent-plugin-kusto", "~> 1.1.3"
54
54
  ```
55
55
 
56
56
  And then execute:
@@ -149,6 +149,7 @@ Modern approach for Kubernetes/AKS workloads. Replaces the legacy Pod Identity s
149
149
  - `workload_identity_client_id`: The client ID for workload identity
150
150
  - `workload_identity_tenant_id`: The tenant ID for workload identity
151
151
  - `workload_identity_token_file_path`: Path to the workload identity token file (optional, defaults to `/var/run/secrets/azure/tokens/azure-identity-token`)
152
+ - `azure_cloud`: The Azure cloud containing the identity and Kusto cluster
152
153
 
153
154
  ## Data Schema and Ingestion Mapping
154
155
 
@@ -239,9 +240,24 @@ This approach provides flexibility to transform the generic 3-column format into
239
240
  | `delayed` | Enable delayed commit for buffer chunks (requires `buffered: true`). | `false` |
240
241
  | `deferred_commit_timeout` | Max time (seconds) to wait for deferred commit verification. | `30` |
241
242
  | `ingestion_mapping_reference` | Name of a pre-defined ingestion mapping in Kusto for data transformation during ingestion. | _none_ |
242
- | `azure_cloud` | Azure cloud environment: `AzureCloud`, `AzureChinaCloud`, `AzureUSGovernmentCloud`, `AzureGermanCloud` | `AzureCloud` |
243
+ | `azure_cloud` | Azure cloud environment: `AzureCloud`, `AzureChinaCloud`, or `AzureUSGovernment` (`AzureUSGovernmentCloud` is accepted as a compatibility alias) | `AzureCloud` |
243
244
  | `logger_path` | File path for plugin logs. If not set, logs to stdout. | stdout |
244
245
 
246
+ ### Sovereign Azure Clouds
247
+
248
+ `azure_cloud` selects the Microsoft Entra authority used by `aad` and `workload_identity` authentication. It does not derive or rewrite the Kusto endpoint, so `endpoint` must be the full endpoint for the same cloud.
249
+
250
+ For Azure US Government, use the canonical cloud name and the Government Kusto endpoint suffix:
251
+
252
+ ```conf
253
+ azure_cloud AzureUSGovernment
254
+ endpoint https://<cluster>.<region>.kusto.usgovcloudapi.net
255
+ ```
256
+
257
+ Existing configurations can also use `AzureUSGovernmentCloud` as an alias. When using `azcli` authentication, select the cloud before signing in with `az cloud set --name AzureUSGovernment`; the plugin's `azure_cloud` setting does not change Azure CLI's active cloud.
258
+
259
+ `AzureGermanCloud` is not supported.
260
+
245
261
  ## Dynamic Table Name Resolution
246
262
 
247
263
  The plugin supports dynamic table name resolution using placeholders in the `table_name` parameter. This allows you to route logs to different tables based on the Fluentd tag.
@@ -445,7 +461,12 @@ This diagram shows the main components and data flow for the plugin, including c
445
461
 
446
462
  ## Release Notes
447
463
 
448
- ### v1.1.2 (Latest)
464
+ ### v1.1.3 (Latest)
465
+ - **Fixed workload identity authentication in sovereign clouds** - The workload identity token provider now builds its token request from the configured `azure_cloud` AAD endpoint instead of always calling the public `login.microsoftonline.com` endpoint, unblocking Azure US Government and Azure China deployments
466
+ - **Added `AzureUSGovernmentCloud` as a recognized `azure_cloud` value** - Accepted alongside the existing `AzureUSGovernment` alias so common Azure US Government naming works out of the box
467
+ - **Added workload identity sovereign cloud test coverage** - New unit tests assert the token endpoint is derived per cloud, plus E2E coverage for the sovereign workload identity flow
468
+
469
+ ### v1.1.2
449
470
  - **Fixed deferred commit with dynamic table names** - Resolved table names are now correctly passed through to `check_data_on_server`, fixing broken ingestion verification when using placeholder-based table names with `delayed: true`
450
471
  - **Improved edge case handling** - Out-of-bounds tag part indices and nil/empty tags now correctly resolve to `"unknown"` fallback instead of empty strings
451
472
  - **Added regression tests** - New test coverage for deferred commit + dynamic table name combination
@@ -7,7 +7,6 @@ require_relative 'tokenprovider_base'
7
7
 
8
8
  class WorkloadIdentity < AbstractTokenProvider
9
9
  DEFAULT_TOKEN_FILE = '/var/run/secrets/azure/tokens/azure-identity-token'
10
- AZURE_OAUTH2_TOKEN_ENDPOINT = 'https://login.microsoftonline.com/%<tenant_id>s/oauth2/v2.0/token'
11
10
 
12
11
  def initialize(outconfiguration)
13
12
  super(outconfiguration)
@@ -22,6 +21,8 @@ class WorkloadIdentity < AbstractTokenProvider
22
21
  @tenant_id = outconfiguration.workload_identity_tenant_id
23
22
  @token_file = outconfiguration.workload_identity_token_file_path || DEFAULT_TOKEN_FILE
24
23
  @kusto_endpoint = outconfiguration.kusto_endpoint
24
+ aad_endpoint = outconfiguration.aad_endpoint.chomp('/')
25
+ @token_request_uri = "#{aad_endpoint}/#{@tenant_id}/oauth2/v2.0/token"
25
26
  @scope = "#{@kusto_endpoint}/.default"
26
27
  end
27
28
 
@@ -35,7 +36,7 @@ class WorkloadIdentity < AbstractTokenProvider
35
36
 
36
37
  def acquire_workload_identity_token
37
38
  oidc_token = read_token_file_safely
38
- uri = URI.parse(format(AZURE_OAUTH2_TOKEN_ENDPOINT, tenant_id: @tenant_id))
39
+ uri = URI.parse(@token_request_uri)
39
40
  req = Net::HTTP::Post.new(uri)
40
41
  req.set_form_data(
41
42
  'grant_type' => 'client_credentials',
@@ -13,7 +13,8 @@ require 'logger'
13
13
  AZURE_CLOUDS = {
14
14
  'AzureCloud' => { 'aad' => 'https://login.microsoftonline.com' },
15
15
  'AzureChinaCloud' => { 'aad' => 'https://login.chinacloudapi.cn' },
16
- 'AzureUSGovernment' => { 'aad' => 'https://login.microsoftonline.us' }
16
+ 'AzureUSGovernment' => { 'aad' => 'https://login.microsoftonline.us' },
17
+ 'AzureUSGovernmentCloud' => { 'aad' => 'https://login.microsoftonline.us' }
17
18
  }.freeze
18
19
 
19
20
  class OutputConfiguration
@@ -3,7 +3,7 @@
3
3
  module Fluent
4
4
  module Plugin
5
5
  module Kusto
6
- VERSION = '1.1.2'
6
+ VERSION = '1.1.3'
7
7
  end
8
8
  end
9
9
  end
@@ -37,6 +37,7 @@ class KustoE2ETest < Test::Unit::TestCase
37
37
  @tenant_id = ENV['TENANT_ID'] || ''
38
38
  @managed_identity_client_id = ENV['MANAGED_IDENTITY_CLIENT_ID'] || ''
39
39
  @auth_type = (ENV['AUTH_TYPE'] || 'aad').downcase
40
+ @azure_cloud = ENV['AZURE_CLOUD'] || 'AzureCloud'
40
41
  @wi_client_id = ENV['WI_CLIENT_ID'] || ''
41
42
  @wi_tenant_id = ENV['WI_TENANT_ID'] || ''
42
43
  @wi_token_file = ENV['WI_TOKEN_FILE'] || ''
@@ -58,7 +59,7 @@ class KustoE2ETest < Test::Unit::TestCase
58
59
  kusto_endpoint: @engine_url,
59
60
  database_name: @database,
60
61
  table_name: @table,
61
- azure_cloud: 'AzureCloud'
62
+ azure_cloud: @azure_cloud
62
63
  }
63
64
 
64
65
  case @auth_type
@@ -95,7 +96,7 @@ class KustoE2ETest < Test::Unit::TestCase
95
96
  ingester.access_token
96
97
  end
97
98
 
98
- def kusto_query(query, type = :data)
99
+ def kusto_query(query, type = :data, raise_on_error: false)
99
100
  endpoint = @engine_url
100
101
  path = type == :management ? '/v1/rest/mgmt' : '/v1/rest/query'
101
102
  uri = URI("#{endpoint}#{path}")
@@ -121,7 +122,10 @@ class KustoE2ETest < Test::Unit::TestCase
121
122
 
122
123
  response = http.request(request)
123
124
  unless response.code.to_i.between?(200, 299)
124
- @logger.error("Kusto query failed with status #{response.code}: #{response.body}")
125
+ error_message = "Kusto query failed with status #{response.code}: #{response.body}"
126
+ @logger.error(error_message)
127
+ raise error_message if raise_on_error
128
+
125
129
  return []
126
130
  end
127
131
 
@@ -133,6 +137,8 @@ class KustoE2ETest < Test::Unit::TestCase
133
137
  rescue JSON::ParserError => e
134
138
  @logger.error("Failed to parse JSON: #{e}")
135
139
  @logger.error(response.body)
140
+ raise if raise_on_error
141
+
136
142
  []
137
143
  end
138
144
  end
@@ -228,6 +234,7 @@ class KustoE2ETest < Test::Unit::TestCase
228
234
  buffered #{config_options[:buffered]}
229
235
  delayed #{config_options[:delayed]}
230
236
  endpoint #{@engine_url}
237
+ azure_cloud #{@azure_cloud}
231
238
  database_name #{@database}
232
239
  table_name #{config_options[:table_name]}
233
240
  compression_enabled #{config_options[:compression_enabled]}
@@ -486,6 +493,16 @@ class KustoE2ETest < Test::Unit::TestCase
486
493
  end
487
494
 
488
495
  # Relaxed authentication resilience test
496
+ test 'Azure US Government workload identity smoke' do
497
+ government_clouds = %w[AzureUSGovernment AzureUSGovernmentCloud]
498
+ government_workload_identity = @auth_type == 'workload_identity' && government_clouds.include?(@azure_cloud)
499
+ omit('Requires Azure US Government workload identity credentials') unless government_workload_identity
500
+
501
+ rows = kusto_query("print smoke_test = 'ok'", raise_on_error: true)
502
+
503
+ assert_equal [['ok']], rows, 'Azure US Government workload identity could not query Kusto'
504
+ end
505
+
489
506
  test 'basic_authentication_resilience' do
490
507
  test_table = "FluentD_auth_basic_#{Time.now.to_i}"
491
508
  configure_and_start_driver(
@@ -58,7 +58,8 @@ class KustoOutputConfigTest < Test::Unit::TestCase
58
58
  clouds = {
59
59
  'AzureCloud' => 'https://login.microsoftonline.com',
60
60
  'AzureChinaCloud' => 'https://login.chinacloudapi.cn',
61
- 'AzureUSGovernment' => 'https://login.microsoftonline.us'
61
+ 'AzureUSGovernment' => 'https://login.microsoftonline.us',
62
+ 'AzureUSGovernmentCloud' => 'https://login.microsoftonline.us'
62
63
  }
63
64
  base_conf_sym = @base_conf.transform_keys(&:to_sym)
64
65
  clouds.each do |cloud, aad_url|
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../helper'
4
+ require 'fluent/plugin/auth/wif_tokenprovider'
5
+ require 'fluent/plugin/conffile'
6
+ require 'mocha/test_unit'
7
+ require 'tempfile'
8
+ require 'uri'
9
+
10
+ class WorkloadIdentityTokenProviderTest < Test::Unit::TestCase
11
+ OUTPUT_CONFIGURATION_CLASS = OutputConfiguration
12
+
13
+ def setup
14
+ @token_file = Tempfile.new('federated-token')
15
+ @token_file.write('federated-token-for-testing')
16
+ @token_file.flush
17
+ end
18
+
19
+ def teardown
20
+ @token_file.close!
21
+ end
22
+
23
+ test 'builds the workload identity token endpoint for each supported cloud' do
24
+ endpoints = {
25
+ 'AzureCloud' => 'https://login.microsoftonline.com/test-tenant/oauth2/v2.0/token',
26
+ 'AzureChinaCloud' => 'https://login.chinacloudapi.cn/test-tenant/oauth2/v2.0/token',
27
+ 'AzureUSGovernment' => 'https://login.microsoftonline.us/test-tenant/oauth2/v2.0/token',
28
+ 'AzureUSGovernmentCloud' => 'https://login.microsoftonline.us/test-tenant/oauth2/v2.0/token'
29
+ }
30
+
31
+ endpoints.each do |cloud, expected_endpoint|
32
+ provider = WorkloadIdentity.new(configuration_for(cloud))
33
+
34
+ assert_equal expected_endpoint, provider.instance_variable_get(:@token_request_uri)
35
+ end
36
+ end
37
+
38
+ test 'exchanges a workload identity assertion against Azure US Government' do
39
+ provider = WorkloadIdentity.new(configuration_for('AzureUSGovernment'))
40
+ response = Net::HTTPOK.new('1.1', '200', 'OK')
41
+ response.stubs(:body).returns({ access_token: 'government-access-token', expires_in: 3600 }.to_json)
42
+ http = mock('Azure US Government token endpoint')
43
+
44
+ provider.expects(:create_http_client).with do |uri|
45
+ assert_equal 'https://login.microsoftonline.us/test-tenant/oauth2/v2.0/token', uri.to_s
46
+ true
47
+ end.returns(http)
48
+ http.expects(:request).with do |request|
49
+ form_data = URI.decode_www_form(request.body).to_h
50
+ assert_equal 'client_credentials', form_data['grant_type']
51
+ assert_equal 'test-client', form_data['client_id']
52
+ assert_equal 'https://test.usgovvirginia.kusto.usgovcloudapi.net/.default', form_data['scope']
53
+ assert_equal 'federated-token-for-testing', form_data['client_assertion']
54
+ true
55
+ end.returns(response)
56
+
57
+ assert_equal 'government-access-token', provider.get_token
58
+ end
59
+
60
+ private
61
+
62
+ def configuration_for(cloud)
63
+ OUTPUT_CONFIGURATION_CLASS.new(
64
+ auth_type: 'workload_identity',
65
+ azure_cloud: cloud,
66
+ workload_identity_client_id: 'test-client',
67
+ workload_identity_tenant_id: 'test-tenant',
68
+ workload_identity_token_file_path: @token_file.path,
69
+ kusto_endpoint: 'https://test.usgovvirginia.kusto.usgovcloudapi.net',
70
+ database_name: 'test-database',
71
+ table_name: 'test-table'
72
+ )
73
+ end
74
+ end
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-kusto
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Komal Rani
8
8
  - Kusto OSS IDC Team
9
- autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2026-02-10 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: bundler
@@ -156,6 +155,7 @@ files:
156
155
  - test/plugin/test_out_kusto_resolve_table_name.rb
157
156
  - test/plugin/test_out_kusto_start.rb
158
157
  - test/plugin/test_out_kusto_try_write.rb
158
+ - test/plugin/test_out_kusto_workload_identity.rb
159
159
  - test/plugin/test_out_kusto_write.rb
160
160
  homepage: https://github.com/Azure/azure-kusto-fluentd
161
161
  licenses:
@@ -163,7 +163,6 @@ licenses:
163
163
  metadata:
164
164
  fluentd_plugin: 'true'
165
165
  fluentd_group: output
166
- post_install_message:
167
166
  rdoc_options: []
168
167
  require_paths:
169
168
  - lib
@@ -178,8 +177,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
178
177
  - !ruby/object:Gem::Version
179
178
  version: '0'
180
179
  requirements: []
181
- rubygems_version: 3.0.3.1
182
- signing_key:
180
+ rubygems_version: 3.7.1
183
181
  specification_version: 4
184
182
  summary: A custom Fluentd output plugin for Azure Kusto ingestion.
185
183
  test_files:
@@ -192,4 +190,5 @@ test_files:
192
190
  - test/plugin/test_out_kusto_resolve_table_name.rb
193
191
  - test/plugin/test_out_kusto_start.rb
194
192
  - test/plugin/test_out_kusto_try_write.rb
193
+ - test/plugin/test_out_kusto_workload_identity.rb
195
194
  - test/plugin/test_out_kusto_write.rb