Skip to content

Commit 50eeedf

Browse files
authored
Merge branch 'ci_prod' into auto/upgrade-go-1.27.1
2 parents a4c24ea + e2cf4a9 commit 50eeedf

17 files changed

Lines changed: 866 additions & 36 deletions

‎Dev Guide.md‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,58 @@ More advanced information needed to develop or build the docker provider will li
44

55
<!-- TODO: eventually move dev info from README.md to here-->
66

7+
## Windows Telegraf dependency
8+
9+
`kubernetes/windows/setup.ps1` installs the official Telegraf 1.40.1 Windows AMD64
10+
ZIP and verifies its pinned SHA256 before extraction. The package corresponds to
11+
upstream commit `26b8f4478b676f4f5e5d8ce1622cdf4f6c273bda`. The existing Windows
12+
pipeline continues to sign `C:\opt\telegraf\telegraf.exe` as an OSS dependency.
13+
14+
The official binary is built with Go 1.27.1 for `windows/amd64`, `GOAMD64=v1`.
15+
Go's [Windows OS floor](https://go.dev/wiki/MinimumRequirements#windows) is Windows
16+
10 or Windows Server 2016 and newer. Both repository image targets, LTSC2019 and
17+
LTSC2022, meet that floor; this does not replace validation inside those images
18+
or in installed-service mode.
19+
20+
The Windows entrypoint registers both Telegraf services through
21+
`telegraf-windows-service.rb`, using the same already-installed `win32-service`
22+
dispatcher as Fluentd. Telegraf 1.40's native service detection requires its
23+
`services.exe` parent to be in session 0, which is not guaranteed in Windows containers.
24+
The host runs the unchanged executable with `--console`, monitors child exit, and
25+
forwards SCM stop through the child's private console/process group. Shutdown is
26+
bounded; the host joins a kill-on-close job before spawning, so the child inherits
27+
containment at creation, including if the host dies before `spawn` returns. The
28+
non-inheritable job handle stays open until host process exit so console cleanup
29+
does not kill the host before SCM shutdown completes. The service
30+
PID is the Ruby host; the Telegraf PID is its child. Role-only host arguments keep
31+
the existing procstat config-path filters selecting Telegraf rather than Ruby.
32+
Per-role logs under `C:\opt\telegraf\logs` rotate at 5 MiB with two backups.
33+
The native startup-boundary regression uses only local sleeping Ruby processes:
34+
`ruby build/windows/installer/scripts/telegraf-windows-console_test.rb`.
35+
It runs on Windows with the image's existing `ffi` dependency and skips on Linux.
36+
37+
Upgrade the two configurations in `build/windows/installer/conf/` and
38+
`tomlparser-prom-customconfig.rb` together with the binary. Windows uses `timeout`
39+
to preserve the overall 15-second metric-scrape limit; upstream's
40+
[1.40 documentation](https://github.com/influxdata/telegraf/blob/e9017dc3266369d6fa185e0e130af1d1d4021ce9/plugins/inputs/prometheus/README.md#L152-L157)
41+
explains that `response_timeout` now covers headers only, unlike the
42+
[1.24.2 client timeout](https://github.com/influxdata/telegraf/blob/9550e7a533dd00632e14435e87ed3eb4b04832c6/plugins/inputs/prometheus/prometheus.go#L254-L261).
43+
Procstat uses `tag_with = ["pid"]` to retain PID tags. Existing `fieldpass`/`fielddrop`
44+
names are unchanged on both OSes because
45+
[1.40 still parses them](https://github.com/influxdata/telegraf/blob/e9017dc3266369d6fa185e0e130af1d1d4021ce9/config/config.go#L1649-L1687).
46+
Linux rendering remains unchanged. Run `ruby build/common/installer/scripts/tomlparser-prom-customconfig_test.rb`
47+
for rendering coverage with and without namespace filters. On Windows, set
48+
`TELEGRAF_WINDOWS_BINARY` to the extracted `telegraf.exe` to also load the generated
49+
configs with that binary in bounded `--test` mode, without Kubernetes access or
50+
running output plugins.
51+
52+
This stock upgrade is a partial mitigation: node discovery rereads the token file
53+
on retries after a failed poll, without relying on file modification time. The
54+
[1.40.0 discovery code](https://github.com/influxdata/telegraf/blob/e9017dc3266369d6fa185e0e130af1d1d4021ce9/plugins/inputs/prometheus/kubernetes.go)
55+
still omits response cleanup on non-200 status codes and lacks an explicit
56+
discovery request timeout. The metric-scrape `timeout` does not bound that path.
57+
HTTP/2 negotiation is not a substitute for fixing those remaining issues.
58+
759
## Testing
860
Last updated 8/18/2021
961

‎build/common/installer/scripts/tomlparser-prom-customconfig.rb‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ def checkForType(variable, varType)
9292

9393
# Telegraf parses interval values with Go's time.ParseDuration, which accepts one or more
9494
# decimal numbers each followed by a unit suffix (for example "30s", "1.5s" or "1h30m").
95-
# The unit set below is the intersection of what the Linux and Windows agents accept; "d"
96-
# is deliberately excluded because the older telegraf shipped on Windows rejects it.
95+
# Keep the existing config-map duration contract on both Linux and Windows; "d" remains
96+
# deliberately excluded even when newer telegraf versions accept days.
9797
# The anchors must be \A and \z (not ^ and $) so that a value such as "1m\n<injected toml>"
9898
# cannot pass validation by matching only its first line.
9999
TELEGRAF_DURATION_REGEX = /\A(?:(?:\d+(?:\.\d+)?|\.\d+)(?:ns|us|\u00B5s|\u03BCs|ms|s|m|h))+\z/
@@ -187,10 +187,6 @@ def createPrometheusPluginsWithNamespaceSetting(monitorKubernetesPods, monitorKu
187187
new_contents = new_contents.gsub("$AZMON_TELEGRAF_CUSTOM_PROM_SCRAPE_SCOPE", "# Commenting this out since new plugins will be created per namespace\n # $AZMON_TELEGRAF_CUSTOM_PROM_SCRAPE_SCOPE")
188188

189189
timeout_config_key = "timeout"
190-
if is_windows?
191-
# For windows, the timeout config key is different because of old version of telegraf
192-
timeout_config_key = "response_timeout"
193-
end
194190

195191
pluginConfigsWithNamespaces = ""
196192
podScrapeScope = (@controller.casecmp(@replicaset) == 0) ? "cluster" : "node"

‎build/common/installer/scripts/tomlparser-prom-customconfig_test.rb‎

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ def run_parser(scenario, configmap)
9191
"SIDECAR_SCRAPING_ENABLED" => nil,
9292
}.merge(spec[:env])
9393

94-
stdout, stderr, = Open3.capture3(env, RbConfig.ruby, parser, chdir: sandbox)
94+
stdout, stderr, status = Open3.capture3(env, RbConfig.ruby, parser, chdir: sandbox)
95+
assert status.success?, "config parser failed: #{stdout}\n#{stderr}"
9596

9697
telemetry_path = File.join(sandbox, "telemetry_prom_config_env_var")
9798
result = {
@@ -170,7 +171,7 @@ def test_invalid_telegraf_durations_are_rejected
170171
"1m;id", # shell metacharacters
171172
"$(id)",
172173
"`id`",
173-
"1d", # rejected: the telegraf shipped on windows does not support days
174+
"1d", # rejected by the existing config-map duration contract
174175
"1", # missing unit
175176
"m", # missing number
176177
"",
@@ -307,6 +308,98 @@ def test_namespace_plugins_are_generated_for_valid_namespaces
307308
assert_no_injected_plugins(:replicaset, result[:conf], "a benign namespace configuration")
308309
end
309310

311+
[:replicaset, :sidecar, :windows].each do |scenario|
312+
{ unfiltered: nil, empty_filter: [], namespace_filtered: ["default", "kube-system"] }.each do |name, namespaces|
313+
define_method("test_prometheus_timeout_and_mapping_#{scenario}_#{name}") do
314+
body = "monitor_kubernetes_pods = true\n" \
315+
"interval = \"45s\"\n" \
316+
"fieldpass = [\"requests_total\"]\n" \
317+
"fielddrop = [\"debug_total\"]\n" \
318+
"kubernetes_label_selector = \"app=metrics\"\n" \
319+
"kubernetes_field_selector = \"spec.nodeName=test-node\""
320+
body += "\nmonitor_kubernetes_pods_namespaces = #{namespaces.inspect}" unless namespaces.nil?
321+
322+
result = run_parser(scenario, configmap_for(scenario, body))
323+
plugins = parse_generated_toml(result[:conf])["inputs"]["prometheus"]
324+
monitored = plugins.select { |plugin| plugin["monitor_kubernetes_pods"] }
325+
expected_namespaces = namespaces.nil? || namespaces.empty? ? [nil] : namespaces
326+
assert_equal expected_namespaces, monitored.map { |plugin| plugin["monitor_kubernetes_pods_namespace"] }
327+
328+
monitored.each do |plugin|
329+
assert_equal "15s", plugin["timeout"], "the overall scrape timeout must remain 15s"
330+
refute plugin.key?("response_timeout"), "response_timeout only bounds response headers in current Telegraf"
331+
assert_equal "45s", plugin["interval"]
332+
assert_equal 2, plugin["metric_version"]
333+
assert_equal "scrapeUrl", plugin["url_tag"]
334+
assert_equal "pod_namespace", plugin["pod_namespace_label_name"]
335+
assert_equal (scenario == :replicaset ? "cluster" : "node"), plugin["pod_scrape_scope"]
336+
assert_equal ["requests_total"], plugin["fieldpass"]
337+
assert_equal ["debug_total"], plugin["fielddrop"]
338+
assert_equal "app=metrics", plugin["kubernetes_label_selector"]
339+
assert_equal "spec.nodeName=test-node", plugin["kubernetes_field_selector"]
340+
end
341+
342+
if scenario == :windows
343+
plugins.each do |plugin|
344+
assert_equal "15s", plugin["timeout"], "the base Windows plugin must also retain the overall timeout"
345+
refute plugin.key?("response_timeout")
346+
end
347+
end
348+
end
349+
end
350+
end
351+
352+
def test_windows_rendered_configs_load_in_packaged_telegraf
353+
binary = ENV["TELEGRAF_WINDOWS_BINARY"]
354+
skip "Set TELEGRAF_WINDOWS_BINARY to run the Windows binary config smoke test" if binary.nil? || binary.empty?
355+
assert File.file?(binary), "TELEGRAF_WINDOWS_BINARY must point to telegraf.exe"
356+
357+
[nil, [], ["default", "kube-system"]].each do |namespaces|
358+
body = "monitor_kubernetes_pods = true\nfieldpass = [\"requests_total\"]\nfielddrop = [\"debug_total\"]"
359+
body += "\nmonitor_kubernetes_pods_namespaces = #{namespaces.inspect}" unless namespaces.nil?
360+
conf = run_parser(:windows, configmap_for(:windows, body))[:conf]
361+
362+
# This smoke test loads the actual generated options without contacting Kubernetes
363+
# or requiring a mounted service-account CA. --test never runs output plugins.
364+
conf = conf.gsub(/^(\s*monitor_kubernetes_pods\s*=\s*)true[ \t]*$/) { "#{Regexp.last_match(1)}false" }
365+
conf = conf.gsub(/^(\s*tls_ca\s*=\s*)"[^"]*"[ \t]*$/) { "#{Regexp.last_match(1)}\"\"" }
366+
367+
Dir.mktmpdir("windows-telegraf-config") do |dir|
368+
path = File.join(dir, "telegraf.conf")
369+
File.write(path, conf)
370+
Open3.popen3({ "NODE_IP" => "127.0.0.1" }, binary, "--console", "--test", "--config", path) do |stdin, stdout, stderr, process| # DevSkim: ignore DS162092 -- Loopback-only config smoke test.
371+
stdin.close
372+
readers = [Thread.new { stdout.read }, Thread.new { stderr.read }]
373+
completed = !process.join(20).nil?
374+
Process.kill("KILL", process.pid) unless completed
375+
output = readers.map(&:value).join("\n")
376+
assert completed, "Telegraf config smoke test exceeded 20 seconds"
377+
assert process.value.success?, "Telegraf rejected the rendered config for #{namespaces.inspect}: #{output}"
378+
end
379+
end
380+
end
381+
end
382+
383+
def test_windows_process_metrics_config_preserves_pid_tags_and_fields
384+
path = File.join(REPO_ROOT, "build/windows/installer/conf/telegraf-ama-logs-process-metrics.conf")
385+
config = Tomlrb.load_file(path)
386+
assert_equal({ "telegraf_role" => "ama-logs-process-metrics" }, config["global_tags"])
387+
assert_equal 11, config["inputs"]["procstat"].length
388+
config["inputs"]["procstat"].each do |plugin|
389+
assert_equal ["pid"], plugin["tag_with"]
390+
refute plugin.key?("pid_tag")
391+
assert_equal ["cpu_usage", "memory_rss"], plugin["fieldpass"]
392+
assert_equal "native", plugin["pid_finder"]
393+
assert_equal "agent_telemetry", plugin["name_override"]
394+
assert_equal "t.azm.ms/", plugin["name_prefix"]
395+
assert_equal "DaemonSet-Windows", plugin["tags"]["ControllerType"]
396+
end
397+
assert_equal(
398+
{ "ai.cloud.role" => "ControllerType", "ai.cloud.roleInstance" => "PodName" },
399+
config["outputs"]["application_insights"].first["context_tag_sources"]
400+
)
401+
end
402+
310403
def test_selectors_are_escaped_in_generated_namespace_plugins
311404
body = "monitor_kubernetes_pods = true\n" \
312405
"monitor_kubernetes_pods_namespaces = [\"default\"]\n" \

‎build/windows/installer/conf/telegraf-ama-logs-process-metrics.conf‎

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
exe = "fluent-bit"
2121
interval = "60s"
2222
pid_finder = "native"
23-
pid_tag = true
23+
tag_with = ["pid"]
2424
name_override = "agent_telemetry"
2525
fieldpass = ["cpu_usage", "memory_rss"]
2626
[inputs.procstat.tags]
@@ -36,7 +36,7 @@
3636
pattern = "telegraf.conf"
3737
interval = "60s"
3838
pid_finder = "native"
39-
pid_tag = true
39+
tag_with = ["pid"]
4040
name_override = "agent_telemetry"
4141
fieldpass = ["cpu_usage", "memory_rss"]
4242
[inputs.procstat.tags]
@@ -52,7 +52,7 @@
5252
pattern = "telegraf-ama-logs-process-metrics.conf"
5353
interval = "60s"
5454
pid_finder = "native"
55-
pid_tag = true
55+
tag_with = ["pid"]
5656
name_override = "agent_telemetry"
5757
fieldpass = ["cpu_usage", "memory_rss"]
5858
[inputs.procstat.tags]
@@ -68,7 +68,7 @@
6868
exe = "MonAgentLauncher"
6969
interval = "60s"
7070
pid_finder = "native"
71-
pid_tag = true
71+
tag_with = ["pid"]
7272
name_override = "agent_telemetry"
7373
fieldpass = ["cpu_usage", "memory_rss"]
7474
[inputs.procstat.tags]
@@ -84,7 +84,7 @@
8484
exe = "MonAgentCore"
8585
interval = "60s"
8686
pid_finder = "native"
87-
pid_tag = true
87+
tag_with = ["pid"]
8888
name_override = "agent_telemetry"
8989
fieldpass = ["cpu_usage", "memory_rss"]
9090
[inputs.procstat.tags]
@@ -100,7 +100,7 @@
100100
exe = "MonAgentHost"
101101
interval = "60s"
102102
pid_finder = "native"
103-
pid_tag = true
103+
tag_with = ["pid"]
104104
name_override = "agent_telemetry"
105105
fieldpass = ["cpu_usage", "memory_rss"]
106106
[inputs.procstat.tags]
@@ -116,7 +116,7 @@
116116
exe = "MonAgentManager"
117117
interval = "60s"
118118
pid_finder = "native"
119-
pid_tag = true
119+
tag_with = ["pid"]
120120
name_override = "agent_telemetry"
121121
fieldpass = ["cpu_usage", "memory_rss"]
122122
[inputs.procstat.tags]
@@ -132,7 +132,7 @@
132132
exe = "AzurePerfCollectorExtension"
133133
interval = "60s"
134134
pid_finder = "native"
135-
pid_tag = true
135+
tag_with = ["pid"]
136136
name_override = "agent_telemetry"
137137
fieldpass = ["cpu_usage", "memory_rss"]
138138
[inputs.procstat.tags]
@@ -148,7 +148,7 @@
148148
exe = "AzureProfilerExtension"
149149
interval = "60s"
150150
pid_finder = "native"
151-
pid_tag = true
151+
tag_with = ["pid"]
152152
name_override = "agent_telemetry"
153153
fieldpass = ["cpu_usage", "memory_rss"]
154154
[inputs.procstat.tags]
@@ -164,7 +164,7 @@
164164
exe = "AggregatorHost"
165165
interval = "60s"
166166
pid_finder = "native"
167-
pid_tag = true
167+
tag_with = ["pid"]
168168
name_override = "agent_telemetry"
169169
fieldpass = ["cpu_usage", "memory_rss"]
170170
[inputs.procstat.tags]
@@ -180,7 +180,7 @@
180180
exe = "powershell"
181181
interval = "60s"
182182
pid_finder = "native"
183-
pid_tag = true
183+
tag_with = ["pid"]
184184
name_override = "agent_telemetry"
185185
fieldpass = ["cpu_usage", "memory_rss"]
186186
[inputs.procstat.tags]

‎build/windows/installer/conf/telegraf.conf‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,8 @@
149149
## OR
150150
# bearer_token_string = "abc_123"
151151

152-
## Specify timeout duration for slower prometheus clients (default is 3s)
153-
response_timeout = "15s"
152+
## Overall HTTP timeout for metric scrapes, including the response body.
153+
timeout = "15s"
154154

155155
## Optional TLS Config
156156
tls_ca = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
require "ffi"
2+
3+
class TelegrafConsole
4+
module Native
5+
extend FFI::Library
6+
ffi_lib "kernel32"
7+
ffi_convention :stdcall
8+
attach_function :GetConsoleProcessList, [:pointer, :ulong], :ulong
9+
attach_function :AllocConsole, [], :bool
10+
attach_function :FreeConsole, [], :bool
11+
attach_function :GenerateConsoleCtrlEvent, [:ulong, :ulong], :bool
12+
attach_function :GetLastError, [], :ulong
13+
attach_function :GetCurrentProcess, [], :pointer
14+
attach_function :CreateJobObjectW, [:pointer, :pointer], :pointer
15+
attach_function :SetInformationJobObject, [:pointer, :int, :pointer, :ulong], :bool
16+
attach_function :AssignProcessToJobObject, [:pointer, :pointer], :bool
17+
attach_function :CloseHandle, [:pointer], :bool
18+
19+
class BasicLimits < FFI::Struct
20+
layout :process_time, :int64, :job_time, :int64, :flags, :uint32,
21+
:min_working_set, :size_t, :max_working_set, :size_t,
22+
:active_processes, :uint32, :affinity, :size_t,
23+
:priority, :uint32, :scheduling, :uint32
24+
end
25+
26+
class ExtendedLimits < FFI::Struct
27+
layout :basic, BasicLimits, :io_counters, [:uint64, 6],
28+
:process_memory, :size_t, :job_memory, :size_t,
29+
:peak_process_memory, :size_t, :peak_job_memory, :size_t
30+
end
31+
end
32+
33+
def prepare
34+
buffer = FFI::MemoryPointer.new(:ulong, 1)
35+
if Native.GetConsoleProcessList(buffer, 1) == 0
36+
unless Native.AllocConsole
37+
raise SystemCallError.new("AllocConsole", Native.GetLastError)
38+
end
39+
@allocated = true
40+
end
41+
@job = Native.CreateJobObjectW(nil, nil)
42+
raise SystemCallError.new("CreateJobObject", Native.GetLastError) if @job.null?
43+
limits = Native::ExtendedLimits.new
44+
limits[:basic][:flags] = 0x2000 # JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE.
45+
unless Native.SetInformationJobObject(@job, 9, limits.pointer, limits.size)
46+
raise SystemCallError.new("SetInformationJobObject", Native.GetLastError)
47+
end
48+
49+
# Enroll the host before spawning: children inherit membership atomically.
50+
# The non-inheritable job handle remains owned only by this host.
51+
unless Native.AssignProcessToJobObject(@job, Native.GetCurrentProcess)
52+
raise SystemCallError.new("AssignProcessToJobObject(host)", Native.GetLastError)
53+
end
54+
@host_assigned = true
55+
end
56+
57+
def interrupt(pid)
58+
Native.GenerateConsoleCtrlEvent(1, pid) # CTRL_BREAK_EVENT for this process group.
59+
end
60+
61+
def close
62+
# Once enrolled, keep the handle until host exit. Closing it here would also
63+
# kill the host before it can finish SCM stop notification and log cleanup.
64+
if @job && !@job.null? && !@host_assigned
65+
unless Native.CloseHandle(@job)
66+
raise SystemCallError.new("CloseHandle(job)", Native.GetLastError)
67+
end
68+
@job = nil
69+
end
70+
return unless @allocated
71+
unless Native.FreeConsole
72+
raise SystemCallError.new("FreeConsole", Native.GetLastError)
73+
end
74+
@allocated = false
75+
end
76+
end

0 commit comments

Comments
 (0)