fluent-plugin-kusto 0.0.1.beta → 0.0.3.beta

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.
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test/unit'
4
+ require 'mocha/test_unit'
5
+ require_relative '../../lib/fluent/plugin/auth/mi_tokenprovider'
6
+
7
+ class DummyConfigForMI
8
+ attr_reader :kusto_endpoint, :managed_identity_client_id
9
+
10
+ def initialize(kusto_endpoint, managed_identity_client_id = nil)
11
+ @kusto_endpoint = kusto_endpoint
12
+ @managed_identity_client_id = managed_identity_client_id
13
+ end
14
+
15
+ def logger
16
+ require 'logger'
17
+ Logger.new($stdout)
18
+ end
19
+ end
20
+
21
+ class ManagedIdentityTokenProviderTest < Test::Unit::TestCase
22
+ def setup
23
+ @resource = 'https://kusto.kusto.windows.net'
24
+ @client_id = '074c3c54-29e2-4230-a81f-333868b8d6ca'
25
+ end
26
+
27
+ def test_initialize_with_user_managed_identity
28
+ config = DummyConfigForMI.new(@resource, @client_id)
29
+ provider = ManagedIdentityTokenProvider.new(config)
30
+
31
+ # Verify instance variables are set correctly
32
+ assert_equal @resource, provider.instance_variable_get(:@resource)
33
+ assert_equal @client_id, provider.instance_variable_get(:@managed_identity_client_id)
34
+ assert_equal false, provider.instance_variable_get(:@use_system_assigned)
35
+ assert_equal true, provider.instance_variable_get(:@use_user_assigned)
36
+ assert_not_nil provider.instance_variable_get(:@token_acquire_url)
37
+ end
38
+
39
+ def test_initialize_with_system_managed_identity
40
+ config = DummyConfigForMI.new(@resource, 'SYSTEM')
41
+ provider = ManagedIdentityTokenProvider.new(config)
42
+
43
+ # Verify instance variables are set correctly for system managed identity
44
+ assert_equal @resource, provider.instance_variable_get(:@resource)
45
+ assert_equal 'SYSTEM', provider.instance_variable_get(:@managed_identity_client_id)
46
+ assert_equal true, provider.instance_variable_get(:@use_system_assigned)
47
+ assert_equal false, provider.instance_variable_get(:@use_user_assigned)
48
+ assert_not_nil provider.instance_variable_get(:@token_acquire_url)
49
+ end
50
+
51
+ def test_initialize_with_empty_client_id
52
+ config = DummyConfigForMI.new(@resource, '')
53
+ provider = ManagedIdentityTokenProvider.new(config)
54
+
55
+ # Verify instance variables are set correctly for empty client_id
56
+ assert_equal @resource, provider.instance_variable_get(:@resource)
57
+ assert_equal '', provider.instance_variable_get(:@managed_identity_client_id)
58
+ assert_equal false, provider.instance_variable_get(:@use_system_assigned)
59
+ assert_equal false, provider.instance_variable_get(:@use_user_assigned)
60
+ end
61
+
62
+ def test_token_acquire_url_formation_user_managed_identity
63
+ config = DummyConfigForMI.new(@resource, @client_id)
64
+ provider = ManagedIdentityTokenProvider.new(config)
65
+
66
+ token_url = provider.instance_variable_get(:@token_acquire_url)
67
+ expected_base = 'http://169.254.169.254/metadata/identity/oauth2/token'
68
+
69
+ assert token_url.include?(expected_base), "Token URL should contain base IMDS endpoint"
70
+ assert token_url.include?('resource=https%3A%2F%2Fkusto.kusto.windows.net'), "Token URL should contain encoded resource"
71
+ assert token_url.include?('api-version=2018-02-01'), "Token URL should contain API version"
72
+ assert token_url.include?("client_id=#{@client_id}"), "Token URL should contain client_id for user managed identity"
73
+ end
74
+
75
+ def test_token_acquire_url_formation_system_managed_identity
76
+ config = DummyConfigForMI.new(@resource, 'SYSTEM')
77
+ provider = ManagedIdentityTokenProvider.new(config)
78
+
79
+ token_url = provider.instance_variable_get(:@token_acquire_url)
80
+ expected_base = 'http://169.254.169.254/metadata/identity/oauth2/token'
81
+
82
+ assert token_url.include?(expected_base), "Token URL should contain base IMDS endpoint"
83
+ assert token_url.include?('resource=https%3A%2F%2Fkusto.kusto.windows.net'), "Token URL should contain encoded resource"
84
+ assert token_url.include?('api-version=2018-02-01'), "Token URL should contain API version"
85
+ assert !token_url.include?('client_id='), "Token URL should NOT contain client_id for system managed identity"
86
+ end
87
+
88
+ def test_fetch_token_success
89
+ config = DummyConfigForMI.new(@resource, @client_id)
90
+ provider = ManagedIdentityTokenProvider.new(config)
91
+
92
+ # Mock successful HTTP response
93
+ mock_response = {
94
+ 'access_token' => 'fake-access-token',
95
+ 'expires_in' => 3600
96
+ }
97
+
98
+ provider.stubs(:post_token_request).returns(mock_response)
99
+
100
+ result = provider.send(:fetch_token)
101
+ assert_equal 'fake-access-token', result[:access_token]
102
+ assert_equal 3600, result[:expires_in]
103
+ end
104
+
105
+ def test_post_token_request_retries_on_failure
106
+ config = DummyConfigForMI.new(@resource, @client_id)
107
+ provider = ManagedIdentityTokenProvider.new(config)
108
+
109
+ # Mock HTTP failure followed by success
110
+ mock_http = mock
111
+ mock_response_fail = mock
112
+ mock_response_fail.stubs(:code).returns(500)
113
+ mock_response_fail.stubs(:body).returns('Internal Server Error')
114
+
115
+ mock_response_success = mock
116
+ mock_response_success.stubs(:code).returns(200)
117
+ mock_response_success.stubs(:body).returns('{"access_token":"fake-token","expires_in":3600}')
118
+
119
+ mock_request = mock
120
+ Net::HTTP::Get.stubs(:new).returns(mock_request)
121
+
122
+ # Mock the HTTP client setup from create_http_client method
123
+ mock_http.stubs(:use_ssl=)
124
+ mock_http.stubs(:open_timeout=)
125
+ mock_http.stubs(:read_timeout=)
126
+ mock_http.stubs(:write_timeout=)
127
+ mock_http.expects(:request).twice.returns(mock_response_fail, mock_response_success)
128
+ Net::HTTP.stubs(:new).returns(mock_http)
129
+
130
+ # Stub sleep to speed up test
131
+ provider.stubs(:sleep)
132
+
133
+ result = provider.send(:post_token_request)
134
+ assert_equal 'fake-token', result['access_token']
135
+ end
136
+
137
+ def test_post_token_request_raises_after_max_retries
138
+ config = DummyConfigForMI.new(@resource, @client_id)
139
+ provider = ManagedIdentityTokenProvider.new(config)
140
+
141
+ # Mock HTTP failure for all attempts
142
+ mock_http = mock
143
+ mock_response_fail = mock
144
+ mock_response_fail.stubs(:code).returns(500)
145
+ mock_response_fail.stubs(:body).returns('Internal Server Error')
146
+
147
+ mock_request = mock
148
+ Net::HTTP::Get.stubs(:new).returns(mock_request)
149
+
150
+ # Mock the HTTP client setup from create_http_client method
151
+ mock_http.stubs(:use_ssl=)
152
+ mock_http.stubs(:open_timeout=)
153
+ mock_http.stubs(:read_timeout=)
154
+ mock_http.stubs(:write_timeout=)
155
+ mock_http.stubs(:request).returns(mock_response_fail)
156
+ Net::HTTP.stubs(:new).returns(mock_http)
157
+
158
+ # Stub sleep to speed up test
159
+ provider.stubs(:sleep)
160
+
161
+ assert_raise(RuntimeError, 'Failed to get managed identity token after 2 attempts.') do
162
+ provider.send(:post_token_request)
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test/unit'
4
+ require 'mocha/test_unit'
5
+ require_relative '../../lib/fluent/plugin/auth/wif_tokenprovider'
6
+
7
+ class DummyConfigForWIF
8
+ attr_reader :kusto_endpoint, :workload_identity_client_id, :workload_identity_tenant_id, :workload_identity_token_file_path
9
+
10
+ def initialize(kusto_endpoint, client_id, tenant_id, token_file_path = nil)
11
+ @kusto_endpoint = kusto_endpoint
12
+ @workload_identity_client_id = client_id
13
+ @workload_identity_tenant_id = tenant_id
14
+ @workload_identity_token_file_path = token_file_path
15
+ end
16
+
17
+ def logger
18
+ require 'logger'
19
+ Logger.new($stdout)
20
+ end
21
+ end
22
+
23
+ class WorkloadIdentityTest < Test::Unit::TestCase
24
+ def setup
25
+ @resource = 'https://kusto.kusto.windows.net'
26
+ @client_id = '074c3c54-29e2-4230-a81f-333868b8d6ca'
27
+ @tenant_id = '12345678-1234-1234-1234-123456789abc'
28
+ @token_file = '/tmp/test-token'
29
+ end
30
+
31
+ def test_initialize_with_custom_token_file
32
+ config = DummyConfigForWIF.new(@resource, @client_id, @tenant_id, @token_file)
33
+ provider = WorkloadIdentity.new(config)
34
+
35
+ # Verify instance variables are set correctly
36
+ assert_equal @resource, provider.instance_variable_get(:@kusto_endpoint)
37
+ assert_equal @client_id, provider.instance_variable_get(:@client_id)
38
+ assert_equal @tenant_id, provider.instance_variable_get(:@tenant_id)
39
+ assert_equal @token_file, provider.instance_variable_get(:@token_file)
40
+ assert_equal "#{@resource}/.default", provider.instance_variable_get(:@scope)
41
+ end
42
+
43
+ def test_initialize_with_default_token_file
44
+ config = DummyConfigForWIF.new(@resource, @client_id, @tenant_id, nil)
45
+ provider = WorkloadIdentity.new(config)
46
+
47
+ # Verify default token file is used
48
+ expected_default = '/var/run/secrets/azure/tokens/azure-identity-token'
49
+ assert_equal expected_default, provider.instance_variable_get(:@token_file)
50
+ end
51
+
52
+ def test_fetch_token_success
53
+ config = DummyConfigForWIF.new(@resource, @client_id, @tenant_id, @token_file)
54
+ provider = WorkloadIdentity.new(config)
55
+
56
+ # Mock successful token acquisition
57
+ mock_response = {
58
+ 'access_token' => 'wif-access-token',
59
+ 'expires_in' => 7200
60
+ }
61
+
62
+ provider.stubs(:acquire_workload_identity_token).returns(mock_response)
63
+
64
+ result = provider.send(:fetch_token)
65
+ assert_equal 'wif-access-token', result[:access_token]
66
+ assert_equal 7200, result[:expires_in]
67
+ end
68
+
69
+ def test_acquire_workload_identity_token_success
70
+ config = DummyConfigForWIF.new(@resource, @client_id, @tenant_id, @token_file)
71
+ provider = WorkloadIdentity.new(config)
72
+
73
+ # Mock file read and HTTP request
74
+ File.stubs(:read).with(@token_file).returns('fake-oidc-token')
75
+
76
+ mock_response = mock
77
+ mock_response.stubs(:is_a?).with(Net::HTTPSuccess).returns(true)
78
+ mock_response.stubs(:body).returns('{"access_token":"wif-token","expires_in":7200}')
79
+
80
+ mock_http = mock
81
+ mock_http.expects(:use_ssl=).with(true)
82
+ mock_http.expects(:open_timeout=).with(10)
83
+ mock_http.expects(:read_timeout=).with(30)
84
+ mock_http.expects(:write_timeout=).with(10)
85
+ mock_http.expects(:request).returns(mock_response)
86
+
87
+ Net::HTTP.stubs(:new).returns(mock_http)
88
+
89
+ result = provider.send(:acquire_workload_identity_token)
90
+ assert_equal 'wif-token', result['access_token']
91
+ assert_equal 7200, result['expires_in']
92
+ end
93
+
94
+ def test_acquire_workload_identity_token_failure
95
+ config = DummyConfigForWIF.new(@resource, @client_id, @tenant_id, @token_file)
96
+ provider = WorkloadIdentity.new(config)
97
+
98
+ # Mock file read and HTTP request failure
99
+ File.stubs(:read).with(@token_file).returns('fake-oidc-token')
100
+
101
+ mock_response = mock
102
+ mock_response.stubs(:is_a?).with(Net::HTTPSuccess).returns(false)
103
+ mock_response.stubs(:code).returns(400)
104
+ mock_response.stubs(:body).returns('Bad Request')
105
+
106
+ mock_http = mock
107
+ mock_http.expects(:use_ssl=).with(true)
108
+ mock_http.expects(:open_timeout=).with(10)
109
+ mock_http.expects(:read_timeout=).with(30)
110
+ mock_http.expects(:write_timeout=).with(10)
111
+ mock_http.expects(:request).returns(mock_response)
112
+
113
+ Net::HTTP.stubs(:new).returns(mock_http)
114
+
115
+ assert_raise(RuntimeError, 'Failed to get access token: 400 Bad Request') do
116
+ provider.send(:acquire_workload_identity_token)
117
+ end
118
+ end
119
+
120
+ def test_oauth2_endpoint_formation
121
+ config = DummyConfigForWIF.new(@resource, @client_id, @tenant_id, @token_file)
122
+ provider = WorkloadIdentity.new(config)
123
+
124
+ # Test that the endpoint is formatted correctly with tenant_id
125
+ File.stubs(:read).with(@token_file).returns('fake-oidc-token')
126
+
127
+ # Mock the rest of the HTTP request to avoid actual network call
128
+ mock_response = mock
129
+ mock_response.stubs(:is_a?).with(Net::HTTPSuccess).returns(true)
130
+ mock_response.stubs(:body).returns('{"access_token":"test","expires_in":3600}')
131
+
132
+ mock_http = mock
133
+ mock_http.expects(:use_ssl=).with(true)
134
+ mock_http.expects(:open_timeout=).with(10)
135
+ mock_http.expects(:read_timeout=).with(30)
136
+ mock_http.expects(:write_timeout=).with(10)
137
+ mock_http.expects(:request).returns(mock_response)
138
+
139
+ Net::HTTP.stubs(:new).returns(mock_http)
140
+
141
+ # Just verify it doesn't crash - the important thing is that setup_config was called
142
+ result = provider.send(:acquire_workload_identity_token)
143
+ assert_equal 'test', result['access_token']
144
+ end
145
+ 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: 0.0.1.beta
4
+ version: 0.0.3.beta
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: 2025-08-20 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
@@ -17,42 +16,62 @@ dependencies:
17
16
  requirements:
18
17
  - - "~>"
19
18
  - !ruby/object:Gem::Version
20
- version: 2.6.9
19
+ version: '2.0'
21
20
  type: :development
22
21
  prerelease: false
23
22
  version_requirements: !ruby/object:Gem::Requirement
24
23
  requirements:
25
24
  - - "~>"
26
25
  - !ruby/object:Gem::Version
27
- version: 2.6.9
26
+ version: '2.0'
28
27
  - !ruby/object:Gem::Dependency
29
28
  name: rake
30
29
  requirement: !ruby/object:Gem::Requirement
31
30
  requirements:
32
31
  - - "~>"
33
32
  - !ruby/object:Gem::Version
34
- version: 13.2.1
33
+ version: '13.0'
35
34
  type: :development
36
35
  prerelease: false
37
36
  version_requirements: !ruby/object:Gem::Requirement
38
37
  requirements:
39
38
  - - "~>"
40
39
  - !ruby/object:Gem::Version
41
- version: 13.2.1
40
+ version: '13.0'
42
41
  - !ruby/object:Gem::Dependency
43
42
  name: test-unit
44
43
  requirement: !ruby/object:Gem::Requirement
45
44
  requirements:
46
45
  - - "~>"
47
46
  - !ruby/object:Gem::Version
48
- version: 3.6.7
47
+ version: '3.0'
49
48
  type: :development
50
49
  prerelease: false
51
50
  version_requirements: !ruby/object:Gem::Requirement
52
51
  requirements:
53
52
  - - "~>"
54
53
  - !ruby/object:Gem::Version
55
- version: 3.6.7
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: fluentd
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '1.0'
62
+ - - "<"
63
+ - !ruby/object:Gem::Version
64
+ version: '2'
65
+ type: :development
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '1.0'
72
+ - - "<"
73
+ - !ruby/object:Gem::Version
74
+ version: '2'
56
75
  - !ruby/object:Gem::Dependency
57
76
  name: fluentd
58
77
  requirement: !ruby/object:Gem::Requirement
@@ -122,25 +141,28 @@ files:
122
141
  - lib/fluent/plugin/client.rb
123
142
  - lib/fluent/plugin/conffile.rb
124
143
  - lib/fluent/plugin/ingester.rb
144
+ - lib/fluent/plugin/kusto_constants.rb
125
145
  - lib/fluent/plugin/kusto_error_handler.rb
126
146
  - lib/fluent/plugin/kusto_query.rb
147
+ - lib/fluent/plugin/kusto_version.rb
127
148
  - lib/fluent/plugin/out_kusto.rb
128
149
  - test/helper.rb
129
150
  - test/plugin/test_azcli_tokenprovider.rb
130
151
  - test/plugin/test_e2e_kusto.rb
152
+ - test/plugin/test_mi_tokenprovider.rb
131
153
  - test/plugin/test_out_kusto_config.rb
132
154
  - test/plugin/test_out_kusto_format.rb
133
155
  - test/plugin/test_out_kusto_process.rb
134
156
  - test/plugin/test_out_kusto_start.rb
135
157
  - test/plugin/test_out_kusto_try_write.rb
136
158
  - test/plugin/test_out_kusto_write.rb
159
+ - test/plugin/test_wif_tokenprovider.rb
137
160
  homepage: https://github.com/Azure/azure-kusto-fluentd
138
161
  licenses:
139
162
  - Apache-2.0
140
163
  metadata:
141
164
  fluentd_plugin: 'true'
142
165
  fluentd_group: output
143
- post_install_message:
144
166
  rdoc_options: []
145
167
  require_paths:
146
168
  - lib
@@ -148,24 +170,25 @@ required_ruby_version: !ruby/object:Gem::Requirement
148
170
  requirements:
149
171
  - - ">="
150
172
  - !ruby/object:Gem::Version
151
- version: '2.5'
173
+ version: 2.7.0
152
174
  required_rubygems_version: !ruby/object:Gem::Requirement
153
175
  requirements:
154
- - - ">"
176
+ - - ">="
155
177
  - !ruby/object:Gem::Version
156
- version: 1.3.1
178
+ version: '0'
157
179
  requirements: []
158
- rubygems_version: 3.0.3.1
159
- signing_key:
180
+ rubygems_version: 3.7.1
160
181
  specification_version: 4
161
182
  summary: A custom Fluentd output plugin for Azure Kusto ingestion.
162
183
  test_files:
163
184
  - test/helper.rb
164
185
  - test/plugin/test_azcli_tokenprovider.rb
165
186
  - test/plugin/test_e2e_kusto.rb
187
+ - test/plugin/test_mi_tokenprovider.rb
166
188
  - test/plugin/test_out_kusto_config.rb
167
189
  - test/plugin/test_out_kusto_format.rb
168
190
  - test/plugin/test_out_kusto_process.rb
169
191
  - test/plugin/test_out_kusto_start.rb
170
192
  - test/plugin/test_out_kusto_try_write.rb
171
193
  - test/plugin/test_out_kusto_write.rb
194
+ - test/plugin/test_wif_tokenprovider.rb