Skip to content

Commit ea5aee9

Browse files
committed
bug 39795650 - preserve DCR parser diagnostics
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 441ffc4e-56a7-490d-9689-12c392813b15
1 parent a72305a commit ea5aee9

4 files changed

Lines changed: 196 additions & 21 deletions

File tree

‎build/linux/installer/scripts/dcr-config-parser.rb‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,14 @@
4848
.map { |stream| stream["stream"] }
4949

5050
logs_and_events_only = streams.none? { |stream| !logs_and_events_streams.include?(stream) }
51-
File.write(output_path, "#{logs_and_events_only}\n")
51+
begin
52+
File.write(output_path, "#{logs_and_events_only}\n")
53+
rescue StandardError => e
54+
ConfigParseErrorLogger.logError(
55+
"Failed to write DCR parser output file '#{output_path}': #{e}"
56+
)
57+
exit 1
58+
end
5259
rescue StandardError => e
5360
ConfigParseErrorLogger.logError("Exception while parsing dcr: #{e}")
5461
exit 1

‎build/linux/installer/scripts/dcr-config-parser_test.rb‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,23 @@ def test_requires_caller_owned_output_path
165165
assert_includes stderr, "Output file path is required"
166166
end
167167

168+
def test_reports_output_file_write_failure_separately
169+
write_dcr(["CONTAINERINSIGHTS_CONTAINERLOGV2"])
170+
unavailable_output = File.join(@sandbox, "missing", "result")
171+
172+
_, stderr, status = Open3.capture3(
173+
RbConfig.ruby,
174+
@parser,
175+
unavailable_output,
176+
chdir: @sandbox
177+
)
178+
179+
refute status.success?
180+
assert_includes stderr, "Failed to write DCR parser output file"
181+
assert_includes stderr, unavailable_output
182+
refute_includes stderr, "Exception while parsing dcr"
183+
end
184+
168185
def test_caller_owns_all_applicability_decisions
169186
write_dcr(["CONTAINERINSIGHTS_CONTAINERLOGV2"])
170187
bypass_environments = [

‎kubernetes/linux/main.sh‎

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -203,37 +203,89 @@ gracefulShutdown() {
203203

204204
DCR_OUTPUT_FILE=""
205205
DCR_VALUE=""
206+
DCR_ERROR=""
206207

207208
cleanupDcrOutput() {
209+
local cleanupoutput
210+
local cleanupstatus
211+
208212
if [ -n "${DCR_OUTPUT_FILE}" ]; then
209-
rm -f -- "${DCR_OUTPUT_FILE}"
213+
cleanupoutput=$(rm -f -- "${DCR_OUTPUT_FILE}" 2>&1)
214+
cleanupstatus=$?
215+
if [ $cleanupstatus -ne 0 ]; then
216+
if [ -n "${DCR_ERROR}" ]; then
217+
DCR_ERROR="${DCR_ERROR}
218+
Failed to remove DCR parser output file '${DCR_OUTPUT_FILE}': ${cleanupoutput}"
219+
else
220+
DCR_ERROR="Failed to remove DCR parser output file '${DCR_OUTPUT_FILE}': ${cleanupoutput}"
221+
fi
222+
return 1
223+
fi
210224
DCR_OUTPUT_FILE=""
211225
fi
226+
return 0
212227
}
213228

214229
parseDcrConfig() {
215230
local parserstatus
231+
local parseroutput
232+
local mktempstatus
233+
local mktempoutput
216234
local outputsize
235+
local outputsizeerror
217236
local expectedsize
218237

219-
cleanupDcrOutput
220-
DCR_OUTPUT_FILE=$(mktemp "${TMPDIR:-/tmp}/dcr_env_var.XXXXXX") || return 1
221-
ruby /opt/dcr-config-parser.rb "${DCR_OUTPUT_FILE}"
238+
DCR_ERROR=""
239+
DCR_VALUE=""
240+
if ! cleanupDcrOutput; then
241+
return 1
242+
fi
243+
mktempoutput=$(mktemp "${TMPDIR:-/tmp}/dcr_env_var.XXXXXX" 2>&1)
244+
mktempstatus=$?
245+
if [ $mktempstatus -ne 0 ]; then
246+
DCR_ERROR="Failed to create DCR parser output file: ${mktempoutput}"
247+
return 1
248+
fi
249+
if [ ! -f "${mktempoutput}" ]; then
250+
DCR_ERROR="Failed to create DCR parser output file: unexpected mktemp output '${mktempoutput}'"
251+
return 1
252+
fi
253+
DCR_OUTPUT_FILE="${mktempoutput}"
254+
parseroutput=$(ruby /opt/dcr-config-parser.rb "${DCR_OUTPUT_FILE}" 2>&1)
222255
parserstatus=$?
256+
if [ $parserstatus -ne 0 ]; then
257+
DCR_ERROR="${parseroutput//config::error::/}"
258+
fi
223259
if [ $parserstatus -eq 0 ]; then
224-
DCR_VALUE=""
225-
IFS= read -r DCR_VALUE < "${DCR_OUTPUT_FILE}"
226-
outputsize=$(wc -c < "${DCR_OUTPUT_FILE}")
227-
case "${DCR_VALUE}" in
228-
true) expectedsize=5 ;;
229-
false) expectedsize=6 ;;
230-
*) expectedsize=-1 ;;
231-
esac
232-
if [ "${outputsize}" -ne "${expectedsize}" ]; then
260+
if ! { IFS= read -r DCR_VALUE < "${DCR_OUTPUT_FILE}"; } 2>/dev/null; then
261+
DCR_ERROR="DCR parser output file is empty or unreadable: '${DCR_OUTPUT_FILE}'"
233262
parserstatus=1
263+
else
264+
outputsizeerror=$({ wc -c < "${DCR_OUTPUT_FILE}"; } 2>&1)
265+
if [ $? -ne 0 ]; then
266+
DCR_ERROR="Failed to inspect DCR parser output file '${DCR_OUTPUT_FILE}': ${outputsizeerror}"
267+
parserstatus=1
268+
else
269+
outputsize="${outputsizeerror//[[:space:]]/}"
270+
if [[ -z "${outputsize}" || "${outputsize}" == *[!0-9]* ]]; then
271+
DCR_ERROR="Failed to inspect DCR parser output file '${DCR_OUTPUT_FILE}': ${outputsizeerror}"
272+
parserstatus=1
273+
else
274+
case "${DCR_VALUE}" in
275+
true) expectedsize=5 ;;
276+
false) expectedsize=6 ;;
277+
*) expectedsize=-1 ;;
278+
esac
279+
if [ "${outputsize}" -ne "${expectedsize}" ]; then
280+
parserstatus=1
281+
fi
282+
fi
283+
fi
234284
fi
235285
fi
236-
cleanupDcrOutput
286+
if ! cleanupDcrOutput; then
287+
parserstatus=1
288+
fi
237289
return $parserstatus
238290
}
239291

@@ -1291,9 +1343,13 @@ fi
12911343

12921344
if [ "${DCR_REQUIRED}" == "true" ]; then
12931345
dcrwaitsecs=0
1294-
until parseDcrConfig > /dev/null 2>&1; do
1346+
until parseDcrConfig; do
12951347
if [ $((dcrwaitsecs % 30)) -eq 0 ]; then
1296-
echo "Required DCR is not available; waiting before retry"
1348+
if [ -n "${DCR_ERROR}" ]; then
1349+
printf '%s\n' "${DCR_ERROR}"
1350+
else
1351+
echo "Required DCR is not available; waiting before retry"
1352+
fi
12971353
fi
12981354
sleep 5
12991355
dcrwaitsecs=$((dcrwaitsecs + 5))

‎kubernetes/linux/main_test.rb‎

Lines changed: 99 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,21 @@ def run_onboarding(info: nil, error: nil, live_process: true)
5656
Open3.capture3(bash_path, "-c", script, chdir: @sandbox)
5757
end
5858

59-
def run_dcr_parser(parser_status: 0, parser_value: "true", write_output: true)
59+
def run_dcr_parser(
60+
parser_status: 0,
61+
parser_value: "true",
62+
parser_error: "",
63+
write_output: true,
64+
create_tmp_dir: true,
65+
remove_status: 0,
66+
remove_output_after_write: false,
67+
initial_dcr_value: ""
68+
)
6069
bin_dir = File.join(@sandbox, "bin")
6170
tmp_dir = File.join(@sandbox, "tmp")
62-
FileUtils.mkdir_p([bin_dir, tmp_dir])
71+
error_capture = File.join(@sandbox, "dcr-error")
72+
FileUtils.mkdir_p(bin_dir)
73+
FileUtils.mkdir_p(tmp_dir) if create_tmp_dir
6374
parser = File.join(bin_dir, "ruby")
6475
File.write(
6576
parser,
@@ -69,17 +80,33 @@ def run_dcr_parser(parser_status: 0, parser_value: "true", write_output: true)
6980
if [ "${WRITE_OUTPUT}" == "true" ]; then
7081
printf '%s\\n' "${PARSER_VALUE}" > "$output"
7182
fi
83+
if [ "${REMOVE_OUTPUT_AFTER_WRITE}" == "true" ]; then
84+
rm -f -- "$output"
85+
fi
86+
printf '%s' "${PARSER_ERROR}" >&2
7287
exit "${PARSER_STATUS}"
7388
SH
7489
)
7590
FileUtils.chmod(0o755, parser)
91+
remove_stub = if remove_status != 0
92+
<<~SH
93+
rm() {
94+
echo "mock removal failure" >&2
95+
return "${REMOVE_STATUS}"
96+
}
97+
SH
98+
else
99+
""
100+
end
76101
script = <<~SH
77102
#{function_source("cleanupDcrOutput")}
78103
#{function_source("parseDcrConfig")}
104+
#{remove_stub}
79105
DCR_OUTPUT_FILE=""
80-
DCR_VALUE=""
106+
DCR_VALUE="${INITIAL_DCR_VALUE}"
81107
parseDcrConfig
82108
status=$?
109+
printf '%s' "$DCR_ERROR" > "#{shell_path(error_capture)}"
83110
printf '%s\\n' "$DCR_VALUE"
84111
printf '%s\\n' "$DCR_OUTPUT_FILE"
85112
exit "$status"
@@ -90,13 +117,19 @@ def run_dcr_parser(parser_status: 0, parser_value: "true", write_output: true)
90117
"TMPDIR" => shell_path(tmp_dir),
91118
"PARSER_STATUS" => parser_status.to_s,
92119
"PARSER_VALUE" => parser_value,
120+
"PARSER_ERROR" => parser_error,
93121
"WRITE_OUTPUT" => write_output.to_s,
122+
"REMOVE_STATUS" => remove_status.to_s,
123+
"REMOVE_OUTPUT_AFTER_WRITE" => remove_output_after_write.to_s,
124+
"INITIAL_DCR_VALUE" => initial_dcr_value,
94125
},
95126
bash_path,
96127
"-c",
97128
script,
98129
chdir: @sandbox
99-
).then { |stdout, stderr, status| [stdout.lines.map(&:chomp), stderr, status, tmp_dir] }
130+
).then do |stdout, stderr, status|
131+
[stdout.lines.map(&:chomp), stderr, status, tmp_dir, File.read(error_capture)]
132+
end
100133
end
101134

102135
def run_set_global_env_var(env_path)
@@ -238,6 +271,68 @@ def test_parse_dcr_config_propagates_parser_failure_and_removes_temporary_output
238271
assert_empty Dir.glob(File.join(tmp_dir, "dcr_env_var.*"))
239272
end
240273

274+
def test_parse_dcr_config_preserves_parser_diagnostic
275+
_, _, status, _, diagnostic = run_dcr_parser(
276+
parser_status: 23,
277+
parser_error: "Failed to write DCR parser output file"
278+
)
279+
280+
refute status.success?
281+
assert_equal "Failed to write DCR parser output file", diagnostic
282+
end
283+
284+
def test_parse_dcr_config_removes_telemetry_error_marker_from_startup_diagnostic
285+
_, _, status, _, diagnostic = run_dcr_parser(
286+
parser_status: 23,
287+
parser_error: "config::error::Failed to write DCR parser output file"
288+
)
289+
290+
refute status.success?
291+
assert_includes diagnostic, "Failed to write DCR parser output file"
292+
refute_includes diagnostic, "config::error::"
293+
end
294+
295+
def test_parse_dcr_config_clears_stale_value_before_failure
296+
output, _, status, = run_dcr_parser(
297+
parser_status: 23,
298+
initial_dcr_value: "true"
299+
)
300+
301+
refute status.success?
302+
assert_equal "", output.first
303+
end
304+
305+
def test_parse_dcr_config_captures_temporary_file_creation_error
306+
_, stderr, status, _, diagnostic = run_dcr_parser(create_tmp_dir: false)
307+
308+
refute status.success?
309+
assert_empty stderr
310+
assert_includes diagnostic, "Failed to create DCR parser output file"
311+
assert_includes diagnostic, "dcr_env_var.XXXXXX"
312+
assert_includes diagnostic, "No such file or directory"
313+
end
314+
315+
def test_parse_dcr_config_captures_temporary_file_cleanup_error
316+
_, stderr, status, _, diagnostic = run_dcr_parser(remove_status: 17)
317+
318+
refute status.success?, "stderr=#{stderr.inspect} diagnostic=#{diagnostic.inspect}"
319+
assert_empty stderr
320+
assert_includes diagnostic, "Failed to remove DCR parser output file"
321+
assert_includes diagnostic, "mock removal failure"
322+
end
323+
324+
def test_parse_dcr_config_captures_output_open_error_without_stderr
325+
_, stderr, status, _, diagnostic = run_dcr_parser(remove_output_after_write: true)
326+
327+
refute status.success?
328+
assert_empty stderr
329+
assert_includes diagnostic, "empty or unreadable"
330+
end
331+
332+
def test_dcr_wait_does_not_redirect_the_function_to_dev_null
333+
refute_match(/until parseDcrConfig\s*>\s*\/dev\/null/, File.read(MAIN_PATH))
334+
end
335+
241336
def test_set_global_env_var_fails_when_state_cannot_be_written
242337
_, _, status = run_set_global_env_var(File.join(@sandbox, "missing", "env_vars"))
243338

0 commit comments

Comments
 (0)