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 +4 -4
- data/README.md +24 -3
- data/lib/fluent/plugin/auth/wif_tokenprovider.rb +3 -2
- data/lib/fluent/plugin/conffile.rb +2 -1
- data/lib/fluent/plugin/kusto_version.rb +1 -1
- data/test/plugin/test_e2e_kusto.rb +20 -3
- data/test/plugin/test_out_kusto_config.rb +2 -1
- data/test/plugin/test_out_kusto_workload_identity.rb +74 -0
- metadata +5 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3190173672cb7fccc00ecf36ff4db6de603cb53a0f25d2f1633d267a072bd9df
|
|
4
|
+
data.tar.gz: 259a7c0b39e550380bbaddd680c5121ea432d72c8f1588a8b16d6ee9c8a3b1e4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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`, `
|
|
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.
|
|
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(
|
|
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
|
|
@@ -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:
|
|
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
|
-
|
|
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.
|
|
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:
|
|
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.
|
|
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
|