-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathinstall_script.sh.template
More file actions
2581 lines (2300 loc) · 98.4 KB
/
install_script.sh.template
File metadata and controls
2581 lines (2300 loc) · 98.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/bin/bash
# (C) Datadog, Inc. 2010-present
# All rights reserved
# Licensed under Apache-2.0 License (see LICENSE)
# Datadog Agent installation script: install and set up the Agent on supported Linux distributions
# using the package manager and Datadog repositories.
set -e
DEPRECATION_MESSAGE_PLACEHOLDER
install_script_version=1.44.0.post
logfile="ddagent-install.log"
support_email=support@datadoghq.com
variant=install_scriptINSTALL_INFO_VERSION_PLACEHOLDER
LEGACY_ETCDIR="/etc/dd-agent"
LEGACY_CONF="$LEGACY_ETCDIR/datadog.conf"
# DATADOG_APT_KEY_CURRENT.public always contains key used to sign current
# repodata and newly released packages
# DATADOG_APT_KEY_382E94DE.public expires in 2022
# DATADOG_APT_KEY_F14F620E.public expires in 2032
# DATADOG_APT_KEY_C0962C7D.public expires in 2028
# DATADOG_APT_KEY_06462314.public expires in 2033
APT_GPG_KEYS=("DATADOG_APT_KEY_CURRENT.public" "DATADOG_APT_KEY_06462314.public" "DATADOG_APT_KEY_C0962C7D.public" "DATADOG_APT_KEY_F14F620E.public" "DATADOG_APT_KEY_382E94DE.public")
# DATADOG_RPM_KEY_CURRENT.public always contains key used to sign current
# repodata and newly released packages
# DATADOG_RPM_KEY_E09422B3.public expires in 2022
# DATADOG_RPM_KEY_FD4BF915.public expires in 2024
# DATADOG_RPM_KEY_B01082D3.public expires in 2028
# DATADOG_RPM_KEY_4F09D16B.public expires in 2033
RPM_GPG_KEYS=("DATADOG_RPM_KEY_CURRENT.public" "DATADOG_RPM_KEY_4F09D16B.public" "DATADOG_RPM_KEY_B01082D3.public" "DATADOG_RPM_KEY_FD4BF915.public" "DATADOG_RPM_KEY_E09422B3.public")
# DATADOG_RPM_KEY.public (4172A230) was only useful to install old (< 6.14) Agent packages.
# We no longer add it and we explicitly remove it.
RPM_GPG_KEYS_TO_REMOVE=("gpg-pubkey-4172a230-55dd14f6")
# Error codes for telemetry
GENERAL_ERROR_CODE=1
UNSUPPORTED_PLATFORM_CODE=5
INVALID_PARAMETERS_CODE=6
UNABLE_TO_INSTALL_DEPENDENCY_CODE=7
# Root user detection
if [ "$UID" == "0" ]; then
sudo_cmd=''
else
sudo_cmd='sudo'
fi
# sudo-rs (used in Ubuntu 25) doesn't support -E flag
# Detect sudo-rs and use explicit env passing instead
if [ -n "$sudo_cmd" ] && sudo --version 2>&1 | sed -n '1p' | grep -q 'sudo-rs'; then
sudo_env_cmd() {
local env_args=()
while IFS='=' read -r name value; do
case "$name" in
DD_*|HTTP_PROXY|HTTPS_PROXY|NO_PROXY|http_proxy|https_proxy|no_proxy)
env_args+=("$name=$value")
;;
esac
done < <(env)
sudo env "${env_args[@]}" "$@"
}
else
# Traditional sudo supports -E flag or no sudo needed
sudo_env_cmd() {
if [ -z "$sudo_cmd" ]; then
"$@"
else
sudo -E "$@"
fi
}
fi
# Trace creation
trace_id=$(od -An -N8 -tu8 < /dev/urandom | tr -d ' ')
start_time=$(date +%s%N)
export DATADOG_TRACE_ID=$trace_id
export DATADOG_PARENT_ID=$trace_id
# Stage tracking globals for detailed telemetry spans
CURRENT_STAGE=""
STAGE_START_TIME=""
CURRENT_STAGE_SPAN_ID=""
STAGE_COUNTER=0
# Format "name:span_id:start_time:duration:error:metadata"
declare -a SCRIPT_STAGES=()
# Set up a named pipe for logging
npipe=/tmp/$$.tmp
mknod $npipe p
# Log all output to a log for error checking
tee <$npipe $logfile &
exec 1>&-
exec 1>$npipe 2>&1
# Set telemetry URL
telemetry_url="https://instrumentation-telemetry-intake.datadoghq.com/api/v2/apmtelemetry"
if [ -n "$DD_SITE" ]; then
telemetry_url="https://instrumentation-telemetry-intake.${DD_SITE}/api/v2/apmtelemetry"
fi
# OS/Distro Detection
# Try lsb_release, fallback with /etc/issue then uname command
KNOWN_DISTRIBUTION="(Debian|Ubuntu|Red Hat|CentOS|openSUSE|Amazon|Arista|SUSE|Rocky|AlmaLinux)"
DISTRIBUTION=$(lsb_release -d 2>/dev/null | grep -Eo "$KNOWN_DISTRIBUTION" || grep -Eo "$KNOWN_DISTRIBUTION" /etc/issue 2>/dev/null || grep -Eo "$KNOWN_DISTRIBUTION" /etc/Eos-release 2>/dev/null || grep -m1 -Eo "$KNOWN_DISTRIBUTION" /etc/os-release 2>/dev/null || uname -s)
function on_exit() {
exit_code=$?
rm -f $npipe
# Handle any remaining active stage
if [ -n "$CURRENT_STAGE" ]; then
end_stage "$exit_code" # End with actual script exit code
fi
report_installer_telemetry "$sudo_cmd" "$DD_API_KEY" "$DD_SITE" "$telemetry_url" "$trace_id" "$logfile" "$start_time" "$exit_code" "$DISTRIBUTION" || true
}
trap on_exit EXIT
##
# REPORTING AND COMMON METHODS
##
function fallback_msg(){
printf "
If you are still having problems, please send an email to $support_email
with the contents of $logfile and any information you think would be
useful and we will do our very best to help you solve your problem.\n"
}
function report(){
if curl -f -sSL --retry 5 \
--data-urlencode "os=${OS}" \
--data-urlencode "version=${agent_major_version}" \
--data-urlencode "log=$(cat $logfile)" \
--data-urlencode "email=${email}" \
--data-urlencode "apikey=${apikey}" \
--data-urlencode "variant=${variant}" \
"$report_failure_url"; then
printf "A notification has been sent to Datadog with the contents of $logfile\n"
else
printf "Unable to send the notification (curl v7.18 or newer is required)"
fi
}
function report_telemetry() {
local install_id="$1"
local install_type="$2"
local install_time="$3"
if [ "$DD_INSTRUMENTATION_TELEMETRY_ENABLED" == "false" ] || \
[ "$site" == "ddog-gov.com" ] || \
[ -z "${apikey}" ] || \
[ -z "$telemetry_url" ]; then
return
fi
install_id_tag=
install_type_tag=
install_time_tag=
if [ -n "$install_id" ] ; then
install_id_tag="\"install_id\": \"$install_id\","
fi
if [ -n "$install_type" ] ; then
install_type_tag="\"install_type\": \"$install_type\","
fi
if [ -n "$install_time" ] ; then
install_time_tag="\"install_time\": $install_time,"
fi
if [ -n "$agent_minor_version" ] ; then
safe_agent_version=$(echo -n "$agent_major_version.$agent_minor_version" | tr '\n' ' ' | tr '"' '_')
else
safe_agent_version=$(echo -n "$agent_major_version" | tr '\n' ' ' | tr '"' '_')
fi
APM_TELEMETRY_SAFE_AGENT_VERSION_OVERRIDE_PLACEHOLDER
if [ -z "${ERROR_CODE}" ] ; then
telemetry_event="
{
\"request_type\": \"apm-onboarding-event\",
\"api_version\": \"v1\",
\"payload\": {
\"event_name\": \"agent.installation.success\",
\"tags\": {
$install_id_tag
$install_type_tag
$install_time_tag
\"agent_platform\": \"native\",
\"agent_version\": \"$safe_agent_version\",
\"script_version\": \"$install_script_version\"
}
}
}
"
else
safe_error_message=$(echo -n "$ERROR_MESSAGE" | tr '\n' ' ' | tr '"' '_')
# Install ID, time and type are typically not reported if the installation does not succeed,
# but if the function is called with those arguments, we will pass them along anyway.
telemetry_event="
{
\"request_type\": \"apm-onboarding-event\",
\"api_version\": \"v1\",
\"payload\": {
\"event_name\": \"agent.installation.error\",
\"tags\": {
$install_id_tag
$install_type_tag
$install_time_tag
\"agent_platform\": \"native\",
\"agent_version\": \"$safe_agent_version\",
\"script_version\": \"$install_script_version\"
},
\"error\": {
\"code\": $ERROR_CODE,
\"message\": \"$safe_error_message\"
}
}
}
"
fi
if ! (cat <<END
$telemetry_event
END
) | curl -f -sSL --retry 5 -o /dev/null \
"$telemetry_url" \
--header 'Content-Type: application/json' \
--header "DD-Api-Key: $apikey" \
--data @-
then
printf "Unable to send telemetry\n"
fi
}
function on_read_error() {
printf "Timed out or input EOF reached, assuming 'No'\n"
yn="n"
}
function get_email() {
emaillocalpart='^[a-zA-Z0-9][a-zA-Z0-9._%+-]{0,63}'
hostnamepart='[a-zA-Z0-9.-]+\.[a-zA-Z]+'
email_regex="$emaillocalpart@$hostnamepart"
cntr=0
until [[ "$cntr" -eq 3 ]]
do
read -p "Enter an email address so we can follow up: " -r email
if [[ "$email" =~ $email_regex ]]; then
isEmailValid=true
break
else
((cntr=cntr+1))
echo -e "\033[33m($cntr/3) Email address invalid: $email\033[0m\n"
fi
done
}
function on_error() {
if [ -z "${ERROR_MESSAGE}" ] ; then
# Save the few lines of the log file for telemetry if the error message is blank
SAVED_ERROR_MESSAGE=$(tail -n 3 $logfile)
fi
printf "\033[31m$ERROR_MESSAGE
It looks like you hit an issue when trying to install the $nice_flavor.
$ERR_SUMMARY
Troubleshooting and basic usage information for the $nice_flavor are available at:
https://docs.datadoghq.com/agent/basic_agent_usage/\n\033[0m\n"
ERROR_MESSAGE=$SAVED_ERROR_MESSAGE
ERROR_CODE=$GENERAL_ERROR_CODE
# Auto-capture current stage failure (integrates with existing error handling)
if [ -n "$CURRENT_STAGE" ]; then
end_stage "${ERROR_CODE:-$GENERAL_ERROR_CODE}" # Use actual error code
fi
report_telemetry
if ! tty -s; then
fallback_msg
exit 1;
fi
if [ "$site" == "ddog-gov.com" ]; then
fallback_msg
exit 1;
fi
while true; do
read -t 60 -p "Do you want to send a failure report to Datadog (including $logfile)? (y/[n]) " -r yn || on_read_error
case $yn in
[Yy]* )
get_email
if [[ -n "$isEmailValid" ]]; then
report
fi
fallback_msg
break;;
[Nn]*|"" )
fallback_msg
break;;
* )
printf "Please answer yes or no.\n"
;;
esac
done
}
trap on_error ERR
function verify_agent_version(){
local ver_separator="$1"
if [ -z "$agent_version_custom" ]; then
ERROR_MESSAGE="Specified version not found: $agent_major_version.$agent_minor_version"
ERROR_CODE=$INVALID_PARAMETERS_CODE
echo -e "
\033[33mWarning: $ERROR_MESSAGE
Check available versions at: https://github.com/DataDog/datadog-agent/blob/main/CHANGELOG.rst\033[0m"
fallback_msg
report_telemetry
exit 1;
else
agent_flavor+="$ver_separator$agent_version_custom"
if [ -n "$DD_OTELCOLLECTOR_ENABLED" ]; then
ddot_package+="$ver_separator$agent_version_custom"
fi
fi
}
function remove_rpm_gpg_keys() {
local sudo_cmd="$1"
shift
local old_keys=("$@")
for key in "${old_keys[@]}"; do
if $sudo_cmd rpm -q "$key" 1>/dev/null 2>/dev/null; then
echo -e "\033[34m\nRemoving old RPM key $key from the RPM database\n\033[0m"
$sudo_cmd rpm --erase "$key"
fi
done
}
# Emulate hashmap with simple switch case
function getMapData() {
if [ "$1" = "flavor_to_readable" ]; then
case "$2" in
"datadog-agent")
DATA="Datadog Agent"
;;
"datadog-iot-agent")
DATA="Datadog IoT Agent"
;;
"datadog-dogstatsd")
DATA="Datadog Dogstatsd"
;;
"datadog-fips-proxy")
DATA="Datadog FIPS Proxy"
;;
"datadog-fips-agent")
DATA="Datadog FIPS Agent"
;;
"datadog-heroku-agent")
DATA="Datadog Heroku Agent"
;;
"datadog-installer")
DATA="Datadog Installer"
;;
*)
DATA="Unknown"
;;
esac
fi
printf '%s' "$DATA"
}
# Create a configuration file with proper ownership and permission if it doesn't already exist
function ensure_config_file_exists() {
local sudo_cmd="$1"
local config_file="$2"
local owner="$3"
if [ -e "$config_file" ]; then
printf "\033[34m\n* Keeping old $config_file configuration file\n\033[0m\n"
return 1
else
$sudo_cmd cp "$config_file.example" "$config_file"
cp_res=$?
$sudo_cmd chown "$owner:dd-agent" "$config_file"
chown_res=$?
$sudo_cmd chmod 640 "$config_file"
chmod_res=$?
return $((cp_res + chown_res + chmod_res))
fi
}
function json_escape() {
local string="$1"
# Escape characters that are special to JSON
string="${string//\\/\\\\}" # Escape backslash
string="${string//\"/\\\"}" # Escape double quote
string="${string//$'\t'/\\t}" # Escape tab
string="${string//$'\n'/\\n}" # Escape newline
string="${string//$'\r'/\\r}" # Escape carriage return
string="${string//$'\b'/\\b}" # Escape backspace
string="${string//$'\f'/\\f}" # Escape form feed
echo "$string"
}
function report_installer_telemetry() {
# Note: do not use local variables from the rest of the script.
# The `trap` method will call this function with the local variables evaluated at the time of the trap.
# You should be able to use environment variables or global variables.
local sudo_cmd="$1"
local apikey="$2"
local site="$3"
local telemetry_url="$4"
local trace_id="$5"
local logfile="$6"
local start_time="$7"
local exit_code="$8"
local distribution="$9"
logs=$(json_escape "$(cat "$logfile")")
local time_now_seconds
local time_now
local time_since_start
local json_logs
local telemetry_trace
local telemetry_logs
if [ "$DD_INSTRUMENTATION_TELEMETRY_ENABLED" == "false" ] || \
[ "$site" == "ddog-gov.com" ] || \
[ -z "${apikey}" ] || \
[ -z "$telemetry_url" ]; then
return
fi
time_now_seconds=$(date +%s)
time_now=$(date +%s%N)
time_since_start=$((time_now - start_time))
# Run all network health checks in parallel to avoid sequential latency
local net_tmpdir net_pids=()
net_tmpdir=$(mktemp -d)
curl -I --max-time 5 -s -o /dev/null -w "%{http_code}" "https://gcr.io/v2/datadoghq/installer-package/manifests/latest" > "$net_tmpdir/gcr_io" 2>/dev/null &
net_pids+=($!)
curl -I --max-time 5 -s -o /dev/null -w "%{http_code}" "https://install.datadoghq.com/scripts/install_script_agent7.sh" > "$net_tmpdir/install_script" 2>/dev/null &
net_pids+=($!)
curl -I --max-time 5 -s -o /dev/null -w "%{http_code}" "https://install.datadoghq.com/index.html" > "$net_tmpdir/install_index" 2>/dev/null &
net_pids+=($!)
curl -I --max-time 5 -s -o /dev/null -w "%{http_code}" "https://public.ecr.aws/datadog/agent/manifests/latest" > "$net_tmpdir/public_ecr" 2>/dev/null &
net_pids+=($!)
curl -I --max-time 5 -s -o /dev/null -w "%{http_code}" "https://yum.datadoghq.com/index.html" > "$net_tmpdir/yum" 2>/dev/null &
net_pids+=($!)
curl -I --max-time 5 -s -o /dev/null -w "%{http_code}" "https://apt.datadoghq.com/index.html" > "$net_tmpdir/apt" 2>/dev/null &
net_pids+=($!)
# Don't let failed network probes abort telemetry reporting — these
# probes are diagnostic data, not prerequisites.
for pid in "${net_pids[@]}"; do
wait "$pid" || true
done
spans_json=$(cat <<-END
{
"service": "datadog-linux-install-script",
"name": "install_script",
"resource": "install_script",
"trace_id": ${trace_id},
"span_id": ${trace_id},
"parent_id": 0,
"start": ${start_time},
"duration": ${time_since_start},
"error": ${exit_code},
"meta": {
"extended_telemetry": "true",
"language": "shell",
"exit_code": ${exit_code},
"version": "${install_script_version}",
"network.gcr_io": "$(json_escape "$(cat "$net_tmpdir/gcr_io" 2>/dev/null)")",
"network.install_datadoghq_com_install_script": "$(json_escape "$(cat "$net_tmpdir/install_script" 2>/dev/null)")",
"network.install_datadoghq_com_index": "$(json_escape "$(cat "$net_tmpdir/install_index" 2>/dev/null)")",
"network.public_ecr_aws": "$(json_escape "$(cat "$net_tmpdir/public_ecr" 2>/dev/null)")",
"network.yum_datadoghq_com": "$(json_escape "$(cat "$net_tmpdir/yum" 2>/dev/null)")",
"network.apt_datadoghq_com": "$(json_escape "$(cat "$net_tmpdir/apt" 2>/dev/null)")"
},
"metrics": {
"_trace_root": 1,
"_top_level": 1,
"_dd.top_level": 1,
"_sampling_priority_v1": 2
}
}
END
)
rm -rf "$net_tmpdir"
# Add stage spans
for stage_info in "${SCRIPT_STAGES[@]}"; do
IFS=':' read -r stage_name stage_span_id stage_start stage_duration stage_error stage_metadata <<< "$stage_info"
# Build stage metadata JSON
local stage_meta_json="\"language\": \"shell\", \"stage\": \"${stage_name}\""
if [ -n "$stage_metadata" ]; then
stage_meta_json="$stage_meta_json, $stage_metadata"
fi
spans_json="$spans_json,
{
\"service\": \"datadog-linux-install-script\",
\"name\": \"${stage_name}\",
\"resource\": \"${stage_name}\",
\"trace_id\": ${trace_id},
\"span_id\": ${stage_span_id},
\"parent_id\": ${trace_id},
\"start\": ${stage_start},
\"duration\": ${stage_duration},
\"error\": ${stage_error:-0},
\"meta\": {
${stage_meta_json}
},
\"metrics\": {
\"_sampling_priority_v1\": 2
}
}"
done
telemetry_trace=$(cat <<-END
{
"api_version": "v2",
"request_type": "traces",
"tracer_time": ${time_now_seconds},
"runtime_id": "${trace_id}",
"seq_id": 1,
"origin": "linux-install-script",
"host": {
"hostname": "$(json_escape "$(uname -n)")",
"os": "$(json_escape "$(uname -o)")",
"distribution": "$(json_escape "$distribution")",
"architecture": "$(json_escape "$(uname -m)")",
"kernel_version": "$(json_escape "$(uname -v)")",
"kernel_name": "$(json_escape "$(uname -s)")",
"kernel_release": "$(json_escape "$(uname -r)")"
},
"application": {
"service_name": "datadog-linux-install-script",
"service_version": "${install_script_version}",
"language_name": "UNKNOWN",
"language_version": "n/a",
"tracer_version": "n/a"
},
"payload": {
"traces": [[
${spans_json}
]]
}
}
END
)
json_logs="[{\"message\": \"$logs\", \"level\": \"DEBUG\", \"trace_id\": \"${trace_id}\", \"span_id\": \"${trace_id}\"}]"
telemetry_logs=$(cat <<-END
{
"api_version": "v2",
"request_type": "logs",
"tracer_time": ${time_now_seconds},
"runtime_id": "${trace_id}",
"seq_id": 2,
"origin": "linux-install-script",
"host": {
"hostname": "$(json_escape "$(uname -n)")",
"os": "$(json_escape "$(uname -o)")",
"distribution": "$(json_escape "$distribution")",
"architecture": "$(json_escape "$(uname -m)")",
"kernel_version": "$(json_escape "$(uname -v)")",
"kernel_name": "$(json_escape "$(uname -s)")",
"kernel_release": "$(json_escape "$(uname -r)")"
},
"application": {
"service_name": "datadog-linux-install-script",
"service_version": "${install_script_version}",
"language_name": "UNKNOWN",
"language_version": "n/a",
"tracer_version": "n/a"
},
"payload": {
"logs": ${json_logs}
}
}
END
)
# Send both telemetry payloads in parallel
local tel_pids=()
(echo "$telemetry_logs" | curl --max-time 10 -f -sSL --retry 5 -o /dev/null \
"$telemetry_url" \
--header 'Content-Type: application/json' \
--header "DD-Api-Key: $apikey" \
--data @- || echo "Unable to send logs telemetry\n") &
tel_pids+=($!)
(echo "$telemetry_trace" | curl --max-time 10 -f -sSL --retry 5 -o /dev/null \
"$telemetry_url" \
--header 'Content-Type: application/json' \
--header "DD-Api-Key: $apikey" \
--data @- || echo "Unable to send trace telemetry\n") &
tel_pids+=($!)
echo "$telemetry_trace" | $sudo_cmd tee /tmp/datadog-installer-trace.json > /dev/null
echo "$telemetry_logs" | $sudo_cmd tee /tmp/datadog-installer-log.json > /dev/null
wait "${tel_pids[@]}"
}
# Begin tracking a new stage
function start_stage() {
local stage_name="$1"
# End previous stage if one was running
if [ -n "$CURRENT_STAGE" ]; then
end_stage 0 # Previous stage completed successfully to reach here
fi
CURRENT_STAGE="$stage_name"
STAGE_START_TIME=$(date +%s%N)
STAGE_COUNTER=$((STAGE_COUNTER + 1))
# Generate stage span ID using seconds + counter to avoid trace_id overflow
local time_now_seconds
time_now_seconds=$(date +%s)
CURRENT_STAGE_SPAN_ID=$((time_now_seconds * 1000 + STAGE_COUNTER))
# Update parent ID so subprocess spans nest under this stage
export DATADOG_PARENT_ID=$CURRENT_STAGE_SPAN_ID
}
# Complete current stage with result, uses existing ERROR_MESSAGE/ERROR_CODE globals
function end_stage() {
if [ -z "$CURRENT_STAGE" ]; then
return # No active stage to end
fi
local stage_error # Error code (0 = success)
local stage_metadata
local current_time
local duration
local safe_error
stage_error="${1:-0}"
stage_metadata="${2:-}"
current_time=$(date +%s%N)
duration=$((current_time - STAGE_START_TIME))
# Build metadata from existing error context if available and no metadata provided
if [ -z "$stage_metadata" ] && [ "$stage_error" -ne 0 ]; then
safe_error=$(printf '%s' "${ERROR_MESSAGE:-Unknown error}" | tr '"' "'" | tr '\n' ' ' | head -c 200)
stage_metadata="\"error\": \"$safe_error\", \"error_code\": \"${ERROR_CODE:-$stage_error}\""
fi
# Store stage info in SCRIPT_STAGES global: "name:span_id:start_time:duration:error:metadata"
SCRIPT_STAGES+=("$CURRENT_STAGE:$CURRENT_STAGE_SPAN_ID:$STAGE_START_TIME:$duration:$stage_error:$stage_metadata")
# Reset stage tracking and restore parent to root trace
CURRENT_STAGE=""
STAGE_START_TIME=""
CURRENT_STAGE_SPAN_ID=""
export DATADOG_PARENT_ID=$trace_id
}
function remove_existing_datadog_installer_package() {
local sudo_cmd="$1"
local os="$2"
local exit_status=0
# find if datadog-installer is installed
if [ "$os" == "Debian" ]; then
$sudo_cmd apt list --installed "datadog-installer" 2>/dev/null | grep -q "datadog-installer" || exit_status=$?
elif [ "$os" == "Red Hat" ]; then
$sudo_cmd yum list installed "datadog-installer" || exit_status=$?
elif [ "$os" == "SUSE" ]; then
$sudo_cmd zypper search -i "datadog-installer" || exit_status=$?
else
return 0
fi
if [ "$exit_status" -ne 0 ]; then
return 0
fi
echo -e "\033[34m\n* Removing the datadog-installer package\n\033[0m"
exit_status=0
if [ "$os" == "Debian" ]; then
$sudo_cmd apt-get remove -y --force-yes "datadog-installer" || exit_status=$?
elif [ "$os" == "Red Hat" ]; then
$sudo_cmd yum -y --disablerepo='*' --enablerepo='datadog' remove "datadog-installer" || $sudo_cmd yum -y remove "datadog-installer" || exit_status=$?
elif [ "$os" == "SUSE" ]; then
$sudo_cmd zypper --non-interactive --no-refresh remove "datadog-installer" || exit_status=$?
else
return 0
fi
if [ "$exit_status" -ne 0 ]; then
echo -e "\033[31m\n* Failed to remove existing datadog-installer package. Please manually remove it and try again\n\033[0m"
exit "$exit_status"
fi
return "$exit_status"
}
function remove_existing_datadog_agent_ddot_package() {
local sudo_cmd="$1"
local os="$2"
local exit_status=0
if [ "$os" == "Debian" ]; then
$sudo_cmd apt list --installed "datadog-agent-ddot" 2>/dev/null | grep -q "datadog-agent-ddot" || exit_status=$?
elif [ "$os" == "Red Hat" ]; then
rpm -q datadog-agent-ddot || exit_status=$?
elif [ "$os" == "SUSE" ]; then
rpm -q datadog-agent-ddot || exit_status=$?
else
return 0
fi
if [ "$exit_status" -ne 0 ]; then
return 0
fi
echo -e "\033[33mRemoving legacy datadog-agent-ddot package before upgrade...\n\033[0m"
exit_status=0
if [ "$os" == "Debian" ]; then
$sudo_cmd apt-get remove -y --force-yes "datadog-agent-ddot" || exit_status=$?
elif [ "$os" == "Red Hat" ]; then
$sudo_cmd yum -y remove "datadog-agent-ddot" || exit_status=$?
elif [ "$os" == "SUSE" ]; then
$sudo_cmd zypper --non-interactive --no-refresh remove "datadog-agent-ddot" || exit_status=$?
fi
return "$exit_status"
}
function remove_existing_packages_for_fips_flavor() {
local sudo_cmd="$1"
local os="$2"
local exit_status=0
# find if fips-proxy or existing agent is installed
if [ "$os" == "Debian" ]; then
$sudo_cmd apt list --installed "datadog-agent" "datadog-fips-proxy" 2>/dev/null | grep -q "datadog" || exit_status=$?
elif [ "$os" == "Red Hat" ]; then
$sudo_cmd yum list installed "datadog-agent" "datadog-fips-proxy" || exit_status=$?
elif [ "$os" == "SUSE" ]; then
$sudo_cmd zypper search -i "datadog-agent" "datadog-fips-proxy" || exit_status=$?
else
return 0
fi
if [ "$exit_status" -ne 0 ]; then
return 0
fi
echo -e "\033[34m\n* Removing the datadog-fips-proxy and existing datadog-agent packages\n\033[0m"
exit_status=0
if [ "$os" == "Debian" ]; then
$sudo_cmd apt-get remove -y --force-yes "datadog-fips-proxy" "datadog-agent" || exit_status=$?
elif [ "$os" == "Red Hat" ]; then
$sudo_cmd yum -y --disablerepo='*' --enablerepo='datadog' remove "datadog-fips-proxy" "datadog-agent" || $sudo_cmd yum -y remove "datadog-fips-proxy" "datadog-agent" || exit_status=$?
elif [ "$os" == "SUSE" ]; then
$sudo_cmd zypper --non-interactive --no-refresh remove "datadog-fips-proxy" "datadog-agent" || exit_status=$?
else
return 0
fi
if [ "$exit_status" -ne 0 ]; then
echo -e "\033[31m\n* Failed to remove existing packages before installing datadog-fips-agent. Please manually remove them and try again\n\033[0m"
exit "$exit_status"
fi
return "$exit_status"
}
function _install_installer_script() {
local installer_url="$1"
# Stream the installer directly into bash via a pipe so that download and
# execution overlap (no temp file). Using a pipe instead of process
# substitution (bash <(...)) lets us inspect PIPESTATUS to detect download
# failures that process substitution would silently swallow.
if command -v curl >/dev/null; then
set +e
curl -L -s -f --retry 3 "$installer_url" \
| DATADOG_TRACE_ID=${DATADOG_TRACE_ID} DATADOG_PARENT_ID=${DATADOG_PARENT_ID} bash 2> >(tee /tmp/datadog-installer-stderr.log >&2 || true > /dev/null) > >(tee /tmp/datadog-installer-stdout.log || true > /dev/null)
local pipestatus=("${PIPESTATUS[@]}")
set -e
if [ "${pipestatus[1]}" -ne 0 ]; then
if [ "${pipestatus[0]}" -ne 0 ]; then
echo "Error: Unable to download the installer script from $installer_url"
else
echo "Error: The installer script failed with exit code ${pipestatus[1]}"
fi
return 1
fi
# curl exit 23 (write error / broken pipe) is benign when bash already
# succeeded — it just means bash closed the pipe before curl finished
# writing. Any other non-zero curl status (e.g. HTTP 404 via -f) with
# bash 0 means the download failed and bash ran on empty stdin.
if [ "${pipestatus[0]}" -ne 0 ] && [ "${pipestatus[0]}" -ne 23 ]; then
echo "Error: Unable to download the installer script from $installer_url"
return 1
fi
elif command -v wget >/dev/null; then
set +e
wget -q --tries=3 -O - "$installer_url" \
| DATADOG_TRACE_ID=${DATADOG_TRACE_ID} DATADOG_PARENT_ID=${DATADOG_PARENT_ID} bash 2> >(tee /tmp/datadog-installer-stderr.log >&2 || true > /dev/null) > >(tee /tmp/datadog-installer-stdout.log || true > /dev/null)
local pipestatus=("${PIPESTATUS[@]}")
set -e
if [ "${pipestatus[1]}" -ne 0 ]; then
if [ "${pipestatus[0]}" -ne 0 ]; then
echo "Error: Unable to download the installer script from $installer_url"
else
echo "Error: The installer script failed with exit code ${pipestatus[1]}"
fi
return 1
fi
# wget exit 3 (file I/O / broken pipe) is benign when bash already
# succeeded — it just means bash closed the pipe before wget finished
# writing. Any other non-zero wget status (e.g. HTTP error) with
# bash 0 means the download failed and bash ran on empty stdin.
if [ "${pipestatus[0]}" -ne 0 ] && [ "${pipestatus[0]}" -ne 3 ]; then
echo "Error: Unable to download the installer script from $installer_url"
return 1
fi
else
echo "Error: Curl or wget is required to install the agent with the specified configuration."
return 1
fi
return 0
}
# install_apm_ssi installs APM Single Step Instrumentation.
function install_apm_ssi() {
local sudo_cmd="$1"
if [ -z "$DD_APM_INSTRUMENTATION_ENABLED" ]; then
return 0
fi
installer_domain=${DD_INSTALLER_REGISTRY_URL_INSTALLER_PACKAGE:-$([[ "$DD_SITE" == "datad0g.com" ]] && echo "install.datad0g.com" || echo "install.datadoghq.com")}
# Technical option to test with non-production install-ssi.sh scripts
if [ -n "$DD_APM_INSTRUMENTATION_PIPELINE_ID" ]; then
installer_domain="${installer_domain}/pipeline-${DD_APM_INSTRUMENTATION_PIPELINE_ID}"
fi
installer_url="https://${installer_domain}/scripts/install-ssi.sh" # TODO: support version pinning?
_install_installer_script "$installer_url" || true
}
start_stage "initialization"
echo -e "\033[34m\n* Datadog INSTALL_SCRIPT_REPORT_VERSION_PLACEHOLDER install script v${install_script_version}\n\033[0m"
##
# AGENT CONFIGURATION OPTIONS
# They are only considered if the configuration file does not already exist (call to `ensure_config_file_exist`)
##
hostname=
if [ -n "$DD_HOSTNAME" ]; then
hostname=$DD_HOSTNAME
fi
site=
if [ -n "$DD_SITE" ]; then
site="$DD_SITE"
fi
apikey=
if [ -n "$DD_API_KEY" ]; then
apikey=$DD_API_KEY
fi
appkey=
if [ -n "$DD_APP_KEY" ]; then
appkey=$DD_APP_KEY
fi
no_start=
if [ -n "$DD_INSTALL_ONLY" ]; then
no_start=true
# When installing rpm packages, scripts might try to start the service
# which we want to avoid
SYSTEMD_OFFLINE=1
fi
no_agent=
if [ -n "$DD_NO_AGENT_INSTALL" ]; then
no_agent=true
fi
infrastructure_mode=
if [ -n "$DD_INFRASTRUCTURE_MODE" ]; then
infrastructure_mode=$DD_INFRASTRUCTURE_MODE
fi
host_tags= # A comma-separated list of tags, e.g. foo:bar,env:prod
if [ -n "$DD_TAGS" ]; then
host_tags=$DD_TAGS
# Falling back to the deprecated DD_HOST_TAGS if DD_TAGS is not set
elif [ -n "$DD_HOST_TAGS" ]; then
host_tags=$DD_HOST_TAGS
fi
if [ -n "$DD_REPO_URL" ]; then
repository_url=$DD_REPO_URL
elif [ -n "$REPO_URL" ]; then
echo -e "\033[33mWarning: REPO_URL is deprecated and might be removed later (use DD_REPO_URL instead).\033[0m"
repository_url=$REPO_URL
else
repository_url="datadoghq.com"
fi
upgrade=
if [ -n "$DD_UPGRADE" ]; then
upgrade=$DD_UPGRADE
fi
fips_mode=
if [ -n "$DD_FIPS_MODE" ]; then
fips_mode=$DD_FIPS_MODE
fi
error_tracking_standalone=
if [ -n "$DD_APM_ERROR_TRACKING_STANDALONE" ]; then
error_tracking_standalone=$DD_APM_ERROR_TRACKING_STANDALONE
fi
dd_env=
if [ -n "$DD_ENV" ]; then
dd_env=$DD_ENV
fi
system_probe_ensure_config=
if [ -n "$DD_SYSTEM_PROBE_ENSURE_CONFIG" ]; then
system_probe_ensure_config=$DD_SYSTEM_PROBE_ENSURE_CONFIG
fi
usm_enabled=
if [ -n "$DD_SYSTEM_PROBE_SERVICE_MONITORING_ENABLED" ]; then
usm_enabled=$DD_SYSTEM_PROBE_SERVICE_MONITORING_ENABLED
fi
agent_flavor="datadog-agent"
if [ -n "$DD_AGENT_FLAVOR" ]; then
agent_flavor=$DD_AGENT_FLAVOR #Eg: datadog-iot-agent
fi
remote_updates=
if [ -n "$DD_REMOTE_UPDATES" ]; then
remote_updates=$DD_REMOTE_UPDATES
fi
par_enabled=
if [ -n "$DD_PRIVATE_ACTION_RUNNER_ENABLED" ]; then
par_enabled=$DD_PRIVATE_ACTION_RUNNER_ENABLED
fi
par_actions_allowlist=
if [ -n "$DD_PRIVATE_ACTION_RUNNER_ACTIONS_ALLOWLIST" ]; then
par_actions_allowlist=$DD_PRIVATE_ACTION_RUNNER_ACTIONS_ALLOWLIST
fi
##
# INSTALL SCRIPT CONFIGURATION OPTIONS
# Technical options to test with non-production values for signature keys, packages or reporting telemetry.
##
if [ -n "$TESTING_KEYS_URL" ]; then
keys_url=$TESTING_KEYS_URL
else
keys_url="keys.datadoghq.com"
fi
if [ -n "$TESTING_YUM_URL" ]; then
yum_url=$TESTING_YUM_URL
else
yum_url="yum.${repository_url}"
fi
# We turn off `repo_gpgcheck` for custom REPO_URL, unless explicitly turned
# on via DD_RPM_REPO_GPGCHECK.
# There is more logic for redhat/suse in their specific code branches below
rpm_repo_gpgcheck=
if [ -n "$DD_RPM_REPO_GPGCHECK" ]; then
rpm_repo_gpgcheck=$DD_RPM_REPO_GPGCHECK