-
Notifications
You must be signed in to change notification settings - Fork 43
/
install.sh
executable file
·928 lines (847 loc) · 32.7 KB
/
install.sh
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
#!/bin/sh
if [ "${DEBUG}" = 1 ]; then
set -x
CURL_LOG="-v"
else
CURL_LOG="-sS"
fi
# Usage:
# curl ... | ENV_VAR=... sh -
# or
# ENV_VAR=... ./install.sh
#
# Environment variables:
# System Agent Variables
# - CATTLE_AGENT_LOGLEVEL (default: info)
# - CATTLE_AGENT_CONFIG_DIR (default: /etc/rancher/agent)
# - CATTLE_AGENT_VAR_DIR (default: /var/lib/rancher/agent)
# - CATTLE_AGENT_BIN_PREFIX (default: /usr/local)
#
# Rancher 2.6+ Variables
# - CATTLE_SERVER
# - CATTLE_TOKEN
# - CATTLE_CA_CHECKSUM
# - CATTLE_ROLE_CONTROLPLANE=false
# - CATTLE_ROLE_ETCD=false
# - CATTLE_ROLE_WORKER=false
# - CATTLE_ROLE_NONE=false
# - CATTLE_LABELS
# - CATTLE_TAINTS
#
# Advanced Environment Variables
# - CATTLE_AGENT_BINARY_BASE_URL (default: latest GitHub release)
# - CATTLE_AGENT_BINARY_URL (default: latest GitHub release)
# - CATTLE_AGENT_UNINSTALL_URL (default: latest GitHub release)
# - CATTLE_PRESERVE_WORKDIR (default: false)
# - CATTLE_REMOTE_ENABLED (default: true)
# - CATTLE_LOCAL_ENABLED (default: false)
# - CATTLE_ID (default: autogenerate)
# - CATTLE_AGENT_BINARY_LOCAL (default: false)
# - CATTLE_AGENT_BINARY_LOCAL_LOCATION (default: )
# - CATTLE_AGENT_UNINSTALL_LOCAL (default: false)
# - CATTLE_AGENT_UNINSTALL_LOCAL_LOCATION (default: )
# - CATTLE_AGENT_STRICT_VERIFY | STRICT_VERIFY (default: false)
# - CATTLE_AGENT_FALLBACK_PATH (default: )
FALLBACK=v0.2.9
CACERTS_PATH=cacerts
RETRYCOUNT=4500
APPLYINATOR_ACTIVE_WAIT_COUNT=60 # If the system-agent is unhealthy but had created an interlock file to indicate it was actively applying a plan, after 5 minutes, ignore the interlock.
DEFAULT_BIN_PREFIX=/usr/local
# info logs the given argument at info log level.
info() {
echo "[INFO] " "$@"
}
# warn logs the given argument at warn log level.
warn() {
echo "[WARN] " "$@" >&2
}
# error logs the given argument at error log level.
error() {
echo "[ERROR] " "$@" >&2
}
# fatal logs the given argument at fatal log level.
fatal() {
echo "[FATAL] " "$@" >&2
exit 1
}
# check_target_mountpoint return success if the target directory is on a dedicated mount point
check_target_mountpoint() {
mountpoint -q "${DEFAULT_BIN_PREFIX}"
}
# check_target_ro returns success if the target directory is read-only
check_target_ro() {
touch "${DEFAULT_BIN_PREFIX}"/.r-sa-ro-test && rm -rf "${DEFAULT_BIN_PREFIX}"/.r-sa-ro-test
test $? -ne 0
}
# check_rootfs_rw returns success if the root filesystem is read-write so we can check for transactional-update system
check_rootfs_rw() {
touch /.rootfs-rw-test && rm -rf /.rootfs-rw-test
test $? -eq 0
}
# parse_args will inspect the argv for --server, --token, --controlplane, --etcd, and --worker, --label x=y, and --taint dead=beef:NoSchedule
parse_args() {
while [ $# -gt 0 ]; do
case "$1" in
"-a" | "--all-roles")
info "All roles requested"
CATTLE_ROLE_CONTROLPLANE=true
CATTLE_ROLE_ETCD=true
CATTLE_ROLE_WORKER=true
shift 1
;;
"-p" | "--controlplane")
info "Role requested: controlplane"
CATTLE_ROLE_CONTROLPLANE=true
shift 1
;;
"-e" | "--etcd")
info "Role requested: etcd"
CATTLE_ROLE_ETCD=true
shift 1
;;
"-w" | "--worker")
info "Role requested: worker"
CATTLE_ROLE_WORKER=true
shift 1
;;
"--no-roles")
info "Role requested: none"
CATTLE_ROLE_NONE=true
shift 1
;;
"-n" | "--node-name")
CATTLE_NODE_NAME="$2"
shift 2
;;
"-a" | "--address")
CATTLE_ADDRESS="$2"
shift 2
;;
"-i" | "--internal-address")
CATTLE_INTERNAL_ADDRESS="$2"
shift 2
;;
"-l" | "--label")
info "Label: $2"
if [ -n "${CATTLE_LABELS}" ]; then
CATTLE_LABELS="${CATTLE_LABELS},$2"
else
CATTLE_LABELS="$2"
fi
shift 2
;;
"--taint" | "--taints")
info "Taint: $2"
if [ -n "${CATTLE_TAINTS}" ]; then
CATTLE_TAINTS="${CATTLE_TAINTS},$2"
else
CATTLE_TAINTS="$2"
fi
shift 2
;;
"-s" | "--server")
CATTLE_SERVER="$2"
shift 2
;;
"-t" | "--token")
CATTLE_TOKEN="$2"
shift 2
;;
"-c" | "--ca-checksum")
CATTLE_CA_CHECKSUM="$2"
shift 2
;;
*)
fatal "Unknown argument passed in ($1)"
;;
esac
done
}
in_no_proxy() {
# Get just the host name/IP
ip_addr="${1#http://}"
ip_addr="${ip_addr#https://}"
ip_addr="${ip_addr%%/*}"
ip_addr="${ip_addr%%:*}"
# If this isn't an IP address, then there is nothing to check
if [ "$(valid_ip "$ip_addr")" = "1" ]; then
echo 1
return
fi
i=1
proxy_ip=$(echo "$NO_PROXY" | cut -d',' -f$i)
while [ -n "$proxy_ip" ]; do
subnet_ip=$(echo "${proxy_ip}" | cut -d'/' -f1)
cidr_mask=$(echo "${proxy_ip}" | cut -d'/' -f2)
if [ "$(valid_ip "$subnet_ip")" = "0" ]; then
# If these were the same, then proxy_ip is an IP address, not a CIDR. curl handles this correctly.
if [ "$cidr_mask" != "$subnet_ip" ]; then
cidr_mask=$(( 32 - cidr_mask ))
shift_multiply=1
while [ "$cidr_mask" -gt 0 ]; do
shift_multiply=$(( shift_multiply * 2 ))
cidr_mask=$(( cidr_mask - 1 ))
done
# Manual left-shift (<<) by original cidr_mask value
netmask=$(( 0xFFFFFFFF * shift_multiply ))
# Apply netmask to both the subnet IP and the given IP address
ip_addr_subnet=$(and "$(ip_to_int "$subnet_ip")" $netmask)
subnet=$(and "$(ip_to_int "$ip_addr")" $netmask)
# Subnet IPs will match if given IP address is in CIDR subnet
if [ "${ip_addr_subnet}" -eq "${subnet}" ]; then
echo 0
return
fi
fi
fi
i=$(( i + 1 ))
proxy_ip=$(echo "$NO_PROXY" | cut -d',' -s -f$i)
done
echo 1
}
# bitwise 'and' in /bin/sh is not supported, so we do it manually.
and() {
ret=0
first=${1}
second=${2}
if [ "$first" -gt "$second" ]; then
tmp=$first
first=$second
second=$tmp
fi
while [ "$first" -gt 0 ]; do
ret=$(( ret * 2 ))
d1=$(( first % 2 ))
d2=$(( second % 2 ))
ans=$(( d1 * d2 ))
if [ "$ans" -eq 1 ]; then
ret=$(( ret + 1 ))
fi
second=$(( second / 2 ))
first=$(( first / 2 ))
done
echo $ret
}
ip_to_int() {
ip_addr="${1}"
ip_1=$(echo "${ip_addr}" | cut -d'.' -f1)
ip_2=$(echo "${ip_addr}" | cut -d'.' -f2)
ip_3=$(echo "${ip_addr}" | cut -d'.' -f3)
ip_4=$(echo "${ip_addr}" | cut -d'.' -f4)
echo $(( $ip_1 * 256*256*256 + $ip_2 * 256*256 + $ip_3 * 256 + $ip_4 ))
}
valid_ip() {
local IP="$1" IFS="." PART
set -- $IP
[ "$#" != 4 ] && echo 1 && return
for PART; do
case "$PART" in
*[!0-9]*) echo 1 && return
esac
[ "$PART" -gt 255 ] && echo 1 && return
done
echo 0
}
setup_env() {
if [ -z "${CATTLE_ROLE_CONTROLPLANE}" ]; then
CATTLE_ROLE_CONTROLPLANE=false
fi
if [ -z "${CATTLE_ROLE_ETCD}" ]; then
CATTLE_ROLE_ETCD=false
fi
if [ -z "${CATTLE_ROLE_WORKER}" ]; then
CATTLE_ROLE_WORKER=false
fi
if [ -z "${CATTLE_ROLE_NONE}" ]; then
CATTLE_ROLE_NONE=false
fi
if [ "${CATTLE_ROLE_NONE}" = "true" ]; then
info "--no-roles flag passed, unsetting all other requested roles"
CATTLE_ROLE_CONTROLPLANE=false
CATTLE_ROLE_ETCD=false
CATTLE_ROLE_WORKER=false
fi
if [ -z "${CATTLE_LOCAL_ENABLED}" ]; then
CATTLE_LOCAL_ENABLED=false
else
CATTLE_LOCAL_ENABLED=$(echo "${CATTLE_LOCAL_ENABLED}" | tr '[:upper:]' '[:lower:]')
fi
if [ -z "${CATTLE_REMOTE_ENABLED}" ]; then
CATTLE_REMOTE_ENABLED=true
else
CATTLE_REMOTE_ENABLED=$(echo "${CATTLE_REMOTE_ENABLED}" | tr '[:upper:]' '[:lower:]')
fi
if [ "${CATTLE_LOCAL_ENABLED}" = "false" ] && [ "${CATTLE_REMOTE_ENABLED}" = "false" ]; then
fatal "Neither local or remote plan support was enabled"
fi
if [ -z "${CATTLE_PRESERVE_WORKDIR}" ]; then
CATTLE_PRESERVE_WORKDIR=false
else
CATTLE_PRESERVE_WORKDIR=$(echo "${CATTLE_PRESERVE_WORKDIR}" | tr '[:upper:]' '[:lower:]')
fi
if [ -z "${CATTLE_AGENT_LOGLEVEL}" ]; then
CATTLE_AGENT_LOGLEVEL=info
else
CATTLE_AGENT_LOGLEVEL=$(echo "${CATTLE_AGENT_LOGLEVEL}" | tr '[:upper:]' '[:lower:]')
fi
if [ "${CATTLE_AGENT_BINARY_LOCAL}" = "true" ]; then
if [ -z "${CATTLE_AGENT_BINARY_LOCAL_LOCATION}" ]; then
fatal "No local binary location was specified"
fi
BINARY_SOURCE=local
else
BINARY_SOURCE=remote
if [ -z "${CATTLE_AGENT_BINARY_URL}" ] && [ -n "${CATTLE_AGENT_BINARY_BASE_URL}" ]; then
CATTLE_AGENT_BINARY_URL="${CATTLE_AGENT_BINARY_BASE_URL}/rancher-system-agent-${ARCH}"
fi
if [ -z "${CATTLE_AGENT_BINARY_URL}" ]; then
if [ $(curl --connect-timeout 60 --max-time 60 -s https://api.github.com/rate_limit | grep '"rate":' -A 4 | grep '"remaining":' | sed -E 's/.*"[^"]+": (.*),/\1/') = 0 ]; then
info "GitHub Rate Limit exceeded, falling back to known good version"
VERSION=$FALLBACK
else
VERSION=$(curl --connect-timeout 60 --max-time 60 -s "https://api.github.com/repos/rancher/system-agent/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
if [ -z "$VERSION" ]; then # Fall back to a known good fallback version because we had an error pulling the latest
info "Error contacting GitHub to retrieve the latest version"
VERSION=$FALLBACK
fi
fi
CATTLE_AGENT_BINARY_URL="https://github.com/rancher/system-agent/releases/download/${VERSION}/rancher-system-agent-${ARCH}"
BINARY_SOURCE=upstream
fi
fi
if [ "${CATTLE_AGENT_UNINSTALL_LOCAL}" = "true" ]; then
if [ -z "${CATTLE_AGENT_UNINSTALL_LOCAL_LOCATION}" ]; then
fatal "No local uninstall location was specified"
fi
UNINSTALL_SOURCE=local
else
UNINSTALL_SOURCE=remote
if [ -z "${CATTLE_AGENT_UNINSTALL_URL}" ] && [ -n "${CATTLE_AGENT_BINARY_BASE_URL}" ]; then
CATTLE_AGENT_UNINSTALL_URL="${CATTLE_AGENT_BINARY_BASE_URL}/system-agent-uninstall.sh"
fi
if [ -z "${CATTLE_AGENT_UNINSTALL_URL}" ]; then
if [ -n "${VERSION}" ]; then
info "Version ${VERSION} used for downloading the rancher-system-agent binary, will reuse for uninstall script"
elif [ $(curl --connect-timeout 60 --max-time 60 -s https://api.github.com/rate_limit | grep '"rate":' -A 4 | grep '"remaining":' | sed -E 's/.*"[^"]+": (.*),/\1/') = 0 ]; then
info "GitHub Rate Limit exceeded, falling back to known good version"
VERSION=$FALLBACK
else
VERSION=$(curl --connect-timeout 60 --max-time 60 -s "https://api.github.com/repos/rancher/system-agent/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
if [ -z "$VERSION" ]; then # Fall back to a known good fallback version because we had an error pulling the latest
info "Error contacting GitHub to retrieve the latest version"
VERSION=$FALLBACK
fi
fi
CATTLE_AGENT_UNINSTALL_URL="https://github.com/rancher/system-agent/releases/download/${VERSION}/system-agent-uninstall.sh"
UNINSTALL_SOURCE=upstream
fi
fi
if [ "${CATTLE_REMOTE_ENABLED}" = "true" ]; then
if [ -z "${CATTLE_TOKEN}" ]; then
fatal "\$CATTLE_TOKEN was not set."
fi
if [ -z "${CATTLE_SERVER}" ]; then
fatal "\$CATTLE_SERVER was not set"
fi
if [ "${CATTLE_ROLE_CONTROLPLANE}" = "false" ] && [ "${CATTLE_ROLE_ETCD}" = "false" ] && [ "${CATTLE_ROLE_WORKER}" = "false" ] && [ "${CATTLE_ROLE_NONE}" = "false" ]; then
fatal "You must select at least one role."
fi
fi
if [ -z "${CATTLE_AGENT_STRICT_VERIFY}" ]; then
CATTLE_AGENT_STRICT_VERIFY=false
if [ -n "${STRICT_VERIFY}" ]; then
CATTLE_AGENT_STRICT_VERIFY=${STRICT_VERIFY}
fi
info "CA strict verification is set to ${CATTLE_AGENT_STRICT_VERIFY}"
fi
if [ -z "${CATTLE_AGENT_CONFIG_DIR}" ]; then
CATTLE_AGENT_CONFIG_DIR=/etc/rancher/agent
info "Using default agent configuration directory ${CATTLE_AGENT_CONFIG_DIR}"
fi
# --- install to /var/lib/rancher/agent by default, except if we are running within transactional-update
# --- in which case we install into /etc/rancher/agent/var as /var is not mounted to the snapshot.
if [ -z "${CATTLE_AGENT_VAR_DIR}" ]; then
if [ -x /usr/sbin/transactional-update ] && check_rootfs_rw; then
CATTLE_AGENT_VAR_DIR=/etc/rancher/agent/var
info "Detected a transactional-update server, using ${CATTLE_AGENT_VAR_DIR} for agent var directory"
else
CATTLE_AGENT_VAR_DIR=/var/lib/rancher/agent
info "Using default agent var directory ${CATTLE_AGENT_VAR_DIR}"
fi
fi
# --- install to /usr/local by default, except if /usr/local is on a separate partition or is read-only
# --- in which case we go into /opt/rancher-system-agent. If we are running within transactional-update
# --- we install to /usr as /usr/local and /opt are not mounted to the snapshot.
if [ -z "${CATTLE_AGENT_BIN_PREFIX}" ]; then
CATTLE_AGENT_BIN_PREFIX="/usr/local"
if check_target_mountpoint || check_target_ro; then
CATTLE_AGENT_BIN_PREFIX="/opt/rancher-system-agent"
warn "/usr/local is read-only or a mount point; installing to ${CATTLE_AGENT_BIN_PREFIX}"
fi
if [ -x /usr/sbin/transactional-update ] && check_rootfs_rw; then
CATTLE_AGENT_BIN_PREFIX=/usr
warn "Detected transactional-update in progress; installing to ${CATTLE_AGENT_BIN_PREFIX}"
fi
fi
CATTLE_ADDRESS=$(get_address "${CATTLE_ADDRESS}")
CATTLE_INTERNAL_ADDRESS=$(get_address "${CATTLE_INTERNAL_ADDRESS}")
}
ensure_directories() {
mkdir -p ${CATTLE_AGENT_VAR_DIR}/interlock
mkdir -p ${CATTLE_AGENT_CONFIG_DIR}
chmod 700 ${CATTLE_AGENT_VAR_DIR}
chmod 700 ${CATTLE_AGENT_VAR_DIR}/interlock
chmod 700 ${CATTLE_AGENT_CONFIG_DIR}
chown root:root ${CATTLE_AGENT_VAR_DIR}
chown root:root ${CATTLE_AGENT_VAR_DIR}/interlock
chown root:root ${CATTLE_AGENT_CONFIG_DIR}
}
# setup_arch set arch and suffix,
# fatal if architecture not supported.
setup_arch() {
case ${ARCH:=$(uname -m)} in
amd64)
ARCH=amd64
SUFFIX=$(uname -s | tr '[:upper:]' '[:lower:]')-${ARCH}
;;
x86_64)
ARCH=amd64
SUFFIX=$(uname -s | tr '[:upper:]' '[:lower:]')-${ARCH}
;;
arm64)
ARCH=arm64
SUFFIX=-${ARCH}
;;
s390x)
ARCH=s390x
SUFFIX=-${ARCH}
;;
aarch64)
ARCH=arm64
SUFFIX=-${ARCH}
;;
arm*)
ARCH=arm
SUFFIX=-${ARCH}hf
;;
*)
fatal "unsupported architecture ${ARCH}"
;;
esac
}
get_address()
{
local address=$1
# If nothing is given, return empty (it will be automatically determined later if empty)
if [ -z $address ]; then
echo ""
# If given address is a network interface on the system, retrieve configured IP on that interface (only the first configured IP is taken)
elif [ -n "$(find /sys/devices -name $address)" ]; then
echo $(ip addr show dev $address | grep -w inet | awk '{print $2}' | cut -f1 -d/ | head -1)
# Loop through cloud provider options to get IP from metadata, if not found return given value
else
noproxy=""
if [ "$(in_no_proxy "169.254.169.254")" -eq 0 ]; then
noproxy="--noproxy '*'"
fi
case $address in
awslocal)
echo $(curl $noproxy --connect-timeout 60 --max-time 60 -s http://169.254.169.254/latest/meta-data/local-ipv4)
;;
awspublic)
echo $(curl $noproxy --connect-timeout 60 --max-time 60 -s http://169.254.169.254/latest/meta-data/public-ipv4)
;;
doprivate)
echo $(curl $noproxy --connect-timeout 60 --max-time 60 -s http://169.254.169.254/metadata/v1/interfaces/private/0/ipv4/address)
;;
dopublic)
echo $(curl $noproxy --connect-timeout 60 --max-time 60 -s http://169.254.169.254/metadata/v1/interfaces/public/0/ipv4/address)
;;
azprivate)
echo $(curl $noproxy --connect-timeout 60 --max-time 60 -s -H Metadata:true "http://169.254.169.254/metadata/instance/network/interface/0/ipv4/ipAddress/0/privateIpAddress?api-version=2017-08-01&format=text")
;;
azpublic)
echo $(curl $noproxy --connect-timeout 60 --max-time 60 -s -H Metadata:true "http://169.254.169.254/metadata/instance/network/interface/0/ipv4/ipAddress/0/publicIpAddress?api-version=2017-08-01&format=text")
;;
gceinternal)
echo $(curl $noproxy --connect-timeout 60 --max-time 60 -s -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/ip)
;;
gceexternal)
echo $(curl $noproxy --connect-timeout 60 --max-time 60 -s -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip)
;;
packetlocal)
echo $(curl $noproxy --connect-timeout 60 --max-time 60 -s https://metadata.packet.net/2009-04-04/meta-data/local-ipv4)
;;
packetpublic)
echo $(curl $noproxy --connect-timeout 60 --max-time 60 -s https://metadata.packet.net/2009-04-04/meta-data/public-ipv4)
;;
ipify)
echo $(curl $noproxy --connect-timeout 60 --max-time 60 -s https://api.ipify.org)
;;
*)
echo $address
;;
esac
fi
}
# verify_downloader verifies existence of
# network downloader executable.
verify_downloader() {
cmd="$(command -v "${1}")"
if [ -z "${cmd}" ]; then
return 1
fi
if [ ! -x "${cmd}" ]; then
return 1
fi
# Set verified executable as our downloader program and return success
DOWNLOADER=${cmd}
return 0
}
# --- write systemd service file ---
create_systemd_service_file() {
info "systemd: Creating service file"
cat <<-EOF >"/etc/systemd/system/rancher-system-agent.service"
[Unit]
Description=Rancher System Agent
Documentation=https://www.rancher.com
Wants=network-online.target
After=network-online.target
[Install]
WantedBy=multi-user.target
[Service]
EnvironmentFile=-/etc/default/rancher-system-agent
EnvironmentFile=-/etc/sysconfig/rancher-system-agent
EnvironmentFile=-/etc/systemd/system/rancher-system-agent.env
Type=simple
Restart=always
RestartSec=5s
Environment=CATTLE_LOGLEVEL=${CATTLE_AGENT_LOGLEVEL}
Environment=CATTLE_AGENT_CONFIG=${CATTLE_AGENT_CONFIG_DIR}/config.yaml
Environment=CATTLE_AGENT_STRICT_VERIFY=${CATTLE_AGENT_STRICT_VERIFY}
ExecStart=${CATTLE_AGENT_BIN_PREFIX}/bin/rancher-system-agent sentinel
EOF
}
download_rancher_files() {
mkdir -p ${CATTLE_AGENT_BIN_PREFIX}/bin
download_rancher_file "rancher-system-agent" "binary" "${CATTLE_AGENT_BINARY_URL}" "${CATTLE_AGENT_BINARY_LOCAL}" "${CATTLE_AGENT_BINARY_LOCAL_LOCATION}" "${BINARY_SOURCE}"
download_rancher_file "rancher-system-agent-uninstall.sh" "script" "${CATTLE_AGENT_UNINSTALL_URL}" "${CATTLE_AGENT_UNINSTALL_LOCAL}" "${CATTLE_AGENT_UNINSTALL_LOCAL_LOCATION}" "${UNINSTALL_SOURCE}"
}
download_rancher_file() {
name=$1
category=$2
url=$3
local=$4
local_location=$5
source=$6
if [ "${local}" = "true" ]; then
info "Using local ${name} ${category} from ${local_location}"
cp -f "${local_location}" "${CATTLE_AGENT_BIN_PREFIX}/bin/${name}"
else
info "Downloading ${name} ${category} from ${url}"
if [ "${source}" != "upstream" ]; then
CURL_BIN_CAFLAG="${CURL_CAFLAG}"
else
CURL_BIN_CAFLAG=""
fi
i=1
while [ "${i}" -ne "${RETRYCOUNT}" ]; do
noproxy=""
if [ "$(in_no_proxy "${url}")" = "0" ]; then
noproxy="--noproxy '*'"
fi
RESPONSE=$(curl $noproxy --connect-timeout 60 --max-time 300 --write-out "%{http_code}\n" ${CURL_BIN_CAFLAG} ${CURL_LOG} -fL "${url}" -o "${CATTLE_AGENT_BIN_PREFIX}/bin/${name}")
case "${RESPONSE}" in
200)
info "Successfully downloaded the ${name} ${category}."
break
;;
*)
i=$((i + 1))
error "$RESPONSE received while downloading the ${name} ${category}. Sleeping for 5 seconds and trying again"
sleep 5
continue
;;
esac
done
chmod +x "${CATTLE_AGENT_BIN_PREFIX}/bin/${name}"
fi
}
check_x509_cert()
{
cert=$1
err=$(openssl x509 -in "${cert}" -noout 2>&1)
if [ $? -eq 0 ]
then
echo ""
else
echo "${err}"
fi
}
validate_ca_checksum() {
if [ -n "${CATTLE_CA_CHECKSUM}" ]; then
CACERT=$(mktemp)
i=1
while [ "${i}" -ne "${RETRYCOUNT}" ]; do
noproxy=""
if [ "$(in_no_proxy ${CATTLE_AGENT_BINARY_URL})" = "0" ]; then
noproxy="--noproxy '*'"
fi
RESPONSE=$(curl $noproxy --connect-timeout 60 --max-time 60 --write-out "%{http_code}\n" --insecure ${CURL_LOG} -fL "${CATTLE_SERVER}/${CACERTS_PATH}" -o ${CACERT})
case "${RESPONSE}" in
200)
info "Successfully downloaded CA certificate"
break
;;
*)
i=$((i + 1))
error "$RESPONSE received while downloading the CA certificate. Sleeping for 5 seconds and trying again"
sleep 5
continue
;;
esac
done
if [ ! -s "${CACERT}" ]; then
error "The environment variable CATTLE_CA_CHECKSUM is set but there is no CA certificate configured at ${CATTLE_SERVER}/${CACERTS_PATH}"
exit 1
fi
err=$(check_x509_cert "${CACERT}")
if [ -n "${err}" ]; then
error "Value from ${CATTLE_SERVER}/${CACERTS_PATH} does not look like an x509 certificate (${err})"
error "Retrieved cacerts:"
cat "${CACERT}"
rm -f "${CACERT}"
exit 1
else
info "Value from ${CATTLE_SERVER}/${CACERTS_PATH} is an x509 certificate"
fi
CATTLE_SERVER_CHECKSUM=$(sha256sum "${CACERT}" | awk '{print $1}')
if [ "${CATTLE_SERVER_CHECKSUM}" != "${CATTLE_CA_CHECKSUM}" ]; then
rm -f "${CACERT}"
error "Configured cacerts checksum ($CATTLE_SERVER_CHECKSUM) does not match given --ca-checksum ($CATTLE_CA_CHECKSUM)"
error "Please check if the correct certificate is configured at${CATTLE_SERVER}/${CACERTS_PATH}"
exit 1
fi
CURL_CAFLAG="--cacert ${CACERT}"
fi
}
validate_rancher_connection() {
RANCHER_SUCCESS=false
if [ -n "${CATTLE_SERVER}" ] && [ "${CATTLE_REMOTE_ENABLED}" = "true" ]; then
i=1
while [ "${i}" -ne "${RETRYCOUNT}" ]; do
noproxy=""
if [ "$(in_no_proxy ${CATTLE_AGENT_BINARY_URL})" = "0" ]; then
noproxy="--noproxy '*'"
fi
RESPONSE=$(curl $noproxy --connect-timeout 60 --max-time 60 --write-out "%{http_code}\n" ${CURL_CAFLAG} ${CURL_LOG} -fL "${CATTLE_SERVER}/healthz" -o /dev/null)
case "${RESPONSE}" in
200)
info "Successfully tested Rancher connection"
RANCHER_SUCCESS=true
break
;;
*)
i=$((i + 1))
error "$RESPONSE received while testing Rancher connection. Sleeping for 5 seconds and trying again"
sleep 5
continue
;;
esac
done
if [ "${RANCHER_SUCCESS}" != "true" ]; then
fatal "Error connecting to Rancher. Perhaps --ca-checksum needs to be set?"
fi
fi
}
validate_ca_required() {
CA_REQUIRED=false
if [ -n "${CATTLE_SERVER}" ] && [ "${CATTLE_REMOTE_ENABLED}" = "true" ]; then
i=1
while [ "${i}" -ne "${RETRYCOUNT}" ]; do
noproxy=""
if [ "$(in_no_proxy ${CATTLE_AGENT_BINARY_URL})" = "0" ]; then
noproxy="--noproxy '*'"
fi
VERIFY_RESULT=$(curl $noproxy --connect-timeout 60 --max-time 60 --write-out "%{ssl_verify_result}\n" ${CURL_LOG} -fL "${CATTLE_SERVER}/healthz" -o /dev/null 2>/dev/null)
CURL_EXIT="$?"
case "${CURL_EXIT}" in
0|60)
case "${VERIFY_RESULT}" in
0)
info "Determined CA is not necessary to connect to Rancher"
CA_REQUIRED=false
CATTLE_CA_CHECKSUM=""
break
;;
*)
i=$((i + 1))
if [ "${CURL_EXIT}" -eq "60" ]; then
info "Determined CA is necessary to connect to Rancher"
CA_REQUIRED=true
break
fi
error "Error received while testing necessity of CA. Sleeping for 5 seconds and trying again"
sleep 5
continue
;;
esac
;;
*)
error "Error while connecting to Rancher to verify CA necessity. Sleeping for 5 seconds and trying again."
sleep 5
continue
;;
esac
done
fi
}
retrieve_connection_info() {
if [ "${CATTLE_REMOTE_ENABLED}" = "true" ]; then
UMASK=$(umask)
umask 0177
i=1
while [ "${i}" -ne "${RETRYCOUNT}" ]; do
noproxy=""
if [ "$(in_no_proxy ${CATTLE_AGENT_BINARY_URL})" = "0" ]; then
noproxy="--noproxy '*'"
fi
RESPONSE=$(curl $noproxy --connect-timeout 60 --max-time 60 --write-out "%{http_code}\n" ${CURL_CAFLAG} ${CURL_LOG} -H "Authorization: Bearer ${CATTLE_TOKEN}" -H "X-Cattle-Id: ${CATTLE_ID}" -H "X-Cattle-Role-Etcd: ${CATTLE_ROLE_ETCD}" -H "X-Cattle-Role-Control-Plane: ${CATTLE_ROLE_CONTROLPLANE}" -H "X-Cattle-Role-Worker: ${CATTLE_ROLE_WORKER}" -H "X-Cattle-Node-Name: ${CATTLE_NODE_NAME}" -H "X-Cattle-Address: ${CATTLE_ADDRESS}" -H "X-Cattle-Internal-Address: ${CATTLE_INTERNAL_ADDRESS}" -H "X-Cattle-Labels: ${CATTLE_LABELS}" -H "X-Cattle-Taints: ${CATTLE_TAINTS}" "${CATTLE_SERVER}"/v3/connect/agent -o ${CATTLE_AGENT_VAR_DIR}/rancher2_connection_info.json)
case "${RESPONSE}" in
200)
info "Successfully downloaded Rancher connection information"
umask "${UMASK}"
return 0
;;
*)
i=$((i + 1))
error "$RESPONSE received while downloading Rancher connection information. Sleeping for 5 seconds and trying again"
sleep 5
continue
;;
esac
done
error "Failed to download Rancher connection information in ${i} attempts"
umask "${UMASK}"
# Clean up invalid rancher2_connection_info.json file
rm -f ${CATTLE_AGENT_VAR_DIR}/rancher2_connection_info.json
return 1
fi
}
generate_config() {
UMASK=$(umask)
umask 0177
cat <<-EOF >"${CATTLE_AGENT_CONFIG_DIR}/config.yaml"
workDirectory: ${CATTLE_AGENT_VAR_DIR}/work
appliedPlanDirectory: ${CATTLE_AGENT_VAR_DIR}/applied
remoteEnabled: ${CATTLE_REMOTE_ENABLED}
localEnabled: ${CATTLE_LOCAL_ENABLED}
localPlanDirectory: ${CATTLE_AGENT_VAR_DIR}/plans
interlockDirectory: ${CATTLE_AGENT_VAR_DIR}/interlock
preserveWorkDirectory: ${CATTLE_PRESERVE_WORKDIR}
EOF
umask "${UMASK}"
if [ "${CATTLE_REMOTE_ENABLED}" = "true" ]; then
echo connectionInfoFile: ${CATTLE_AGENT_VAR_DIR}/rancher2_connection_info.json >> "${CATTLE_AGENT_CONFIG_DIR}/config.yaml"
fi
}
generate_cattle_identifier() {
if [ -z "${CATTLE_ID}" ]; then
info "Generating Cattle ID"
if [ -f "${CATTLE_AGENT_CONFIG_DIR}/cattle-id" ]; then
CATTLE_ID=$(cat ${CATTLE_AGENT_CONFIG_DIR}/cattle-id);
if [ -z "${CATTLE_ID}" ]; then
fatal "Cattle ID was empty, aborting installation"
fi
info "Cattle ID was already detected as ${CATTLE_ID}. Not generating a new one."
return
fi
CATTLE_ID=$(dd if=/dev/urandom count=1 bs=512 2>/dev/null | sha256sum | awk '{print $1}' | head -c 63);
UMASK=$(umask)
umask 0177
echo "${CATTLE_ID}" > ${CATTLE_AGENT_CONFIG_DIR}/cattle-id
umask "${UMASK}"
if [ ! -s ${CATTLE_AGENT_CONFIG_DIR}/cattle-id ]; then
fatal "Cattle ID could not be persisted. Aborting installation"
fi
return
fi
info "Not generating Cattle ID"
}
ensure_systemd_service_stopped() {
if systemctl is-active --quiet rancher-system-agent.service; then
info "Rancher System Agent was detected on this host. Ensuring the rancher-system-agent is stopped."
systemctl stop rancher-system-agent
fi
}
create_env_file() {
FILE_SA_ENV="/etc/systemd/system/rancher-system-agent.env"
info "Creating environment file ${FILE_SA_ENV}"
install -m 0600 /dev/null "${FILE_SA_ENV}"
for i in "HTTP_PROXY" "HTTPS_PROXY" "NO_PROXY"; do
eval v=\"\$$i\"
if [ -z "${v}" ]; then
env | grep -E -i "^${i}" | tee -a ${FILE_SA_ENV} >/dev/null
else
echo "$i=$v" | tee -a ${FILE_SA_ENV} >/dev/null
fi
done
# if /usr/local/ is ready only or on a separate partition, we want to add the bin dirs of rke2/k3s to our path
if check_target_mountpoint || check_target_ro; then
info "${DEFAULT_BIN_PREFIX} is unsuitable for installation: adding fallback path to systemd unit env file."
if [ -n "${CATTLE_AGENT_FALLBACK_PATH}" ]; then
echo "PATH=${PATH}:${CATTLE_AGENT_FALLBACK_PATH}" | tee -a ${FILE_SA_ENV} >/dev/null
else
echo "PATH=${PATH}:/opt/rke2/bin:/opt/bin" | tee -a ${FILE_SA_ENV} >/dev/null
fi
fi
}
ensure_applyinator_not_active() {
i=1
while [ "${i}" -ne "${APPLYINATOR_ACTIVE_WAIT_COUNT}" ]; do
if [ -f "${CATTLE_AGENT_VAR_DIR}/interlock/applyinator-active" ]; then
i=$((i + 1))
info "Active plan reconciliation detected. Sleeping for 5 seconds and retrying check"
sleep 5
continue
fi
break
done
}
do_install() {
if [ $(id -u) != 0 ]; then
fatal "This script must be run as root."
fi
parse_args "$@"
setup_arch
setup_env
ensure_directories
verify_downloader curl || fatal "can not find curl for downloading files"
touch ${CATTLE_AGENT_VAR_DIR}/interlock/restart-pending
ensure_applyinator_not_active
if [ -z "${CATTLE_CA_CHECKSUM}" ] && [ $(echo "${CATTLE_AGENT_STRICT_VERIFY}" | tr '[:upper:]' '[:lower:]') = "true" ]; then
fatal "Aborting system-agent installation due to requested strict CA verification with no CA checksum provided"
fi
if [ -n "${CATTLE_CA_CHECKSUM}" ] && [ $(echo "${CATTLE_AGENT_STRICT_VERIFY}" | tr '[:upper:]' '[:lower:]') != "true" ]; then
validate_ca_required
fi
validate_ca_checksum
validate_rancher_connection
ensure_systemd_service_stopped
download_rancher_files
generate_config
if [ -n "${CATTLE_TOKEN}" ]; then
generate_cattle_identifier
retrieve_connection_info || fatal "Aborting system-agent installation due to failure to retrieve Rancher connection information"
fi
create_systemd_service_file
create_env_file
systemctl daemon-reload >/dev/null
info "Enabling rancher-system-agent.service"
systemctl enable rancher-system-agent
info "Starting/restarting rancher-system-agent.service"
systemctl restart rancher-system-agent
rm -f ${CATTLE_AGENT_VAR_DIR}/interlock/restart-pending
}
do_install "$@"
exit 0