aws_manager/ec2/plugins/
scripts.rs

1use std::io::{self, Error, ErrorKind};
2
3use crate::ec2::{ArchType, OsType};
4
5pub fn start(os_type: OsType) -> io::Result<String> {
6    match os_type {
7        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok("#!/usr/bin/env bash
8
9# print all executed commands to terminal
10set -x
11
12# do not mask errors in a pipeline
13set -o pipefail
14
15# treat unset variables as an error
16set -o nounset
17
18# exit script whenever it errs
19set -o errexit
20
21# makes the  default answers be used for all questions
22export DEBIAN_FRONTEND=noninteractive
23
24############################################
25### Machine Architecture ###################
26############################################
27# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
28# 'dpkg --print-architecture' to decide amd64/arm64
29# 'uname -m' to decide x86_64/aarch64
30
31MACHINE=$(uname -m)
32if [ \"$MACHINE\" == \"x86_64\" ]; then
33    ARCH=\"amd64\"
34elif [ \"$MACHINE\" == \"aarch64\" ]; then
35    ARCH=\"arm64\"
36else
37    echo \"Unknown machine architecture '$MACHINE'\" >&2
38    exit 1
39fi
40echo MACHINE: $MACHINE
41echo ARCH: $ARCH
42
43# running as root, in /, check CPU/OS/host info
44whoami
45pwd
46lscpu
47cat /etc/os-release
48hostnamectl
49
50############################################
51### Basic packages #########################
52############################################
53
54sudo mkdir -p /etc/systemd/system
55sudo chown -R ubuntu:ubuntu /etc/systemd/system
56
57while [ 1 ]; do
58    sudo apt-get update -yq
59    sudo apt-get upgrade -yq
60    sudo apt-get install -yq \\
61    build-essential tmux git xclip htop zsh \\
62    jq curl wget \\
63    zip unzip gzip tar \\
64    libssl-dev \\
65    pkg-config lsb-release vim \\
66    linux-headers-$(uname -r)
67    sudo apt-get clean
68    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
69    sleep 2s;
70done;
71while [ 1 ]; do
72    sudo apt update
73    sudo apt clean all
74    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
75    sleep 2s;
76done;
77
78# /usr/sbin/iptables
79which iptables
80iptables --version
81
82# /usr/sbin/iptables-save
83which iptables-save
84iptables-save --version
85
86# /usr/sbin/iptables-restore
87which iptables-restore
88iptables-restore --version
89
90/usr/bin/gcc --version
91/usr/bin/c++ -v
92
93if ! command -v lsb_release &> /dev/null
94then
95    echo \"lsb_release could not be found\"
96    exit 1
97fi
98lsb_release --all
99
100# sudo sh -c \"$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)\"
101# chsh -s /bin/zsh
102# sudo chown -R ubuntu /home/ubuntu/.cache
103# sudo chown -R ubuntu /home/ubuntu/.zshrc
104# sudo chown -R ubuntu /home/ubuntu/.zsh_history
105
106mkdir -p /home/ubuntu/.vim
107sudo chown -R ubuntu /home/ubuntu/.vim
108"
109        .to_string()),
110        _ => Err(Error::new(
111            ErrorKind::InvalidInput,
112            format!("os_type '{}' not supported", os_type.as_str()),
113        )),
114    }
115}
116
117pub fn update_bash_profile(
118    os_type: OsType,
119    anaconda_installed: bool,
120    python_installed: bool,
121    rust_installed: bool,
122    cuda_toolkit_installed: bool,
123    go_installed: bool,
124    kubectl_installed: bool,
125    helm_installed: bool,
126    data_directory_mounted: bool,
127) -> io::Result<String> {
128    match os_type {
129        OsType::Ubuntu2004 | OsType::Ubuntu2204 => {
130            let mut paths = Vec::new();
131            if anaconda_installed {
132                paths.push("/home/ubuntu/anaconda3/bin".to_string());
133            }
134            if python_installed {
135                paths.push("/home/ubuntu/.local/bin".to_string());
136            }
137            if rust_installed {
138                paths.push("/home/ubuntu/.cargo/bin".to_string());
139            }
140            if cuda_toolkit_installed {
141                paths.push("/usr/local/cuda-12.2/bin".to_string());
142            }
143
144            let mut profile = String::from(
145                // https://stackoverflow.com/questions/27920806/how-to-avoid-heredoc-expanding-variables
146                "cat << 'EOF' >> /home/ubuntu/.profile
147HISTSIZE=1000000
148HISTFILESIZE=2000000
149
150alias ..='cd ..'
151alias hh='history | grep'
152alias t=tmux
153alias kill-tmux='tmux list-sessions; tmux kill-session -a;'
154alias kill-docker='docker kill $(docker ps -q)'
155alias clean-docker='docker system prune --all --force; docker rmi $(docker images -a -q);'
156alias pbcopy='xclip -selection clipboard'
157alias gith='git rev-parse HEAD; git rev-parse HEAD | pbcopy'
158
159export VISUAL=vim
160export EDITOR=vim
161export GPG_TTY=$(tty)
162
163",
164            );
165            let mut bashrc = String::from(
166                // https://stackoverflow.com/questions/27920806/how-to-avoid-heredoc-expanding-variables
167                "cat << 'EOF' >> /home/ubuntu/.bashrc
168HISTSIZE=1000000
169HISTFILESIZE=2000000
170
171alias ..='cd ..'
172alias hh='history | grep'
173alias t=tmux
174alias kill-tmux='tmux list-sessions; tmux kill-session -a;'
175alias kill-docker='docker kill $(docker ps -q)'
176alias clean-docker='docker system prune --all --force; docker rmi $(docker images -a -q);'
177alias pbcopy='xclip -selection clipboard'
178alias gith='git rev-parse HEAD; git rev-parse HEAD | pbcopy'
179
180export VISUAL=vim
181export EDITOR=vim
182export GPG_TTY=$(tty)
183
184",
185            );
186
187            if go_installed {
188                paths.push("/usr/local/go/bin".to_string());
189                paths.push("/home/ubuntu/go/bin".to_string());
190
191                profile.push_str(
192                    "export GOPATH=/home/ubuntu/go
193",
194                );
195                bashrc.push_str(
196                    "export GOPATH=/home/ubuntu/go
197",
198                );
199            }
200
201            if kubectl_installed {
202                profile.push_str(
203                    "alias k=kubectl
204",
205                );
206                bashrc.push_str(
207                    "alias k=kubectl
208",
209                );
210            }
211            if helm_installed {
212                profile.push_str(
213                    "alias h=helm
214",
215                );
216                bashrc.push_str(
217                    "alias h=helm
218",
219                );
220            }
221
222            let path_line = format!(
223                "export PATH={}:$PATH
224",
225                paths.join(":")
226            );
227
228            profile.push_str(&path_line);
229            if rust_installed {
230                // only include in the profile
231                profile.push_str(
232                    ". /opt/rust/env
233",
234                )
235            }
236            bashrc.push_str(&path_line);
237            if rust_installed {
238                // only include in the bashrc
239                bashrc.push_str(
240                    ". /opt/rust/env
241",
242                )
243            }
244
245            // add "path_line" once more to use the PATH
246            // during the following executions
247            if data_directory_mounted {
248                Ok(format!(
249                    "
250###########################
251# setting up user bash profiles
252
253{profile}
254# set permissions
255sudo chown -R $(whoami) /data
256sudo chown -R ubuntu /data
257EOF
258
259{bashrc}
260# set permissions
261sudo chown -R $(whoami) /data
262sudo chown -R ubuntu /data
263EOF
264
265{path_line}"
266                ))
267            } else {
268                Ok(format!(
269                    "
270###########################
271# setting up user bash profiles
272
273{profile}
274EOF
275
276{bashrc}
277EOF
278
279{path_line}"
280                ))
281            }
282        }
283        _ => Err(Error::new(
284            ErrorKind::InvalidInput,
285            format!("os_type '{}' not supported", os_type.as_str()),
286        )),
287    }
288}
289
290pub fn imds(os_type: OsType) -> io::Result<String> {
291    match os_type {
292        OsType::Ubuntu2004 | OsType::Ubuntu2204 | OsType::Al2023 => Ok("
293###########################
294# install imds utils
295# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
296# https://github.com/awslabs/amazon-eks-ami/tree/master/files/bin
297
298while [ 1 ]; do
299    rm -f /tmp/imds || true;
300    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/bin/imds\"
301    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
302    sleep 2s;
303done;
304
305chmod +x /tmp/imds
306sudo mv /tmp/imds /usr/bin/imds
307"
308        .to_string()),
309        _ => Err(Error::new(
310            ErrorKind::InvalidInput,
311            format!("os_type '{}' not supported", os_type.as_str()),
312        )),
313    }
314}
315
316pub fn provider_id(os_type: OsType) -> io::Result<String> {
317    match os_type {
318        OsType::Ubuntu2004 | OsType::Ubuntu2204 | OsType::Al2023 => Ok("
319###########################
320# install provider-id utils
321# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
322# https://github.com/awslabs/amazon-eks-ami/tree/master/files/bin
323
324while [ 1 ]; do
325    rm -f /tmp/provider-id || true;
326    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/bin/provider-id\"
327    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
328    sleep 2s;
329done;
330
331chmod +x /tmp/provider-id
332sudo mv /tmp/provider-id /usr/bin/provider-id
333"
334        .to_string()),
335        _ => Err(Error::new(
336            ErrorKind::InvalidInput,
337            format!("os_type '{}' not supported", os_type.as_str()),
338        )),
339    }
340}
341
342pub fn vercmp(os_type: OsType) -> io::Result<String> {
343    match os_type {
344        OsType::Ubuntu2004 | OsType::Ubuntu2204 | OsType::Al2023 => Ok("
345###########################
346# install vercmp utils
347# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
348# https://github.com/awslabs/amazon-eks-ami/tree/master/files/bin
349
350while [ 1 ]; do
351    rm -f /tmp/vercmp || true;
352    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/bin/vercmp\"
353    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
354    sleep 2s;
355done;
356
357chmod +x /tmp/vercmp
358sudo mv /tmp/vercmp /usr/bin/vercmp
359"
360        .to_string()),
361        _ => Err(Error::new(
362            ErrorKind::InvalidInput,
363            format!("os_type '{}' not supported", os_type.as_str()),
364        )),
365    }
366}
367
368pub fn setup_local_disks(os_type: OsType) -> io::Result<String> {
369    match os_type {
370        OsType::Ubuntu2004 | OsType::Ubuntu2204 | OsType::Al2023 => Ok("
371###########################
372# install setup-local-disks utils
373# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
374# https://github.com/awslabs/amazon-eks-ami/tree/master/files/bin
375
376while [ 1 ]; do
377    rm -f /tmp/setup-local-disks || true;
378    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/bin/setup-local-disks\"
379    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
380    sleep 2s;
381done;
382
383chmod +x /tmp/setup-local-disks
384sudo mv /tmp/setup-local-disks /usr/bin/setup-local-disks
385"
386        .to_string()),
387        _ => Err(Error::new(
388            ErrorKind::InvalidInput,
389            format!("os_type '{}' not supported", os_type.as_str()),
390        )),
391    }
392}
393
394pub fn mount_bpf_fs(os_type: OsType) -> io::Result<String> {
395    match os_type {
396        OsType::Ubuntu2004 | OsType::Ubuntu2204 | OsType::Al2023 => Ok("
397###########################
398# install mount-bpf-fs utils
399# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
400# https://github.com/awslabs/amazon-eks-ami/tree/master/files/bin
401
402while [ 1 ]; do
403    rm -f /tmp/mount-bpf-fs || true;
404    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/bin/mount-bpf-fs\"
405    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
406    sleep 2s;
407done;
408
409chmod +x /tmp/mount-bpf-fs
410sudo mv /tmp/mount-bpf-fs /usr/bin/mount-bpf-fs
411"
412        .to_string()),
413        _ => Err(Error::new(
414            ErrorKind::InvalidInput,
415            format!("os_type '{}' not supported", os_type.as_str()),
416        )),
417    }
418}
419
420pub fn time_sync(os_type: OsType) -> io::Result<String> {
421    match os_type {
422        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok("
423###########################
424# install time sync utils
425
426# https://github.com/awslabs/amazon-eks-ami/tree/master/files/bin/configure-clocksource
427while [ 1 ]; do
428    rm -f /tmp/configure-clocksource || true;
429    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/bin/configure-clocksource\"
430    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
431    sleep 2s;
432done;
433chmod +x /tmp/configure-clocksource
434sudo mv /tmp/configure-clocksource /usr/bin/configure-clocksource
435
436# https://github.com/awslabs/amazon-eks-ami/commit/056e31f8c7477e893424abce468cb32bbcd1f079#diff-049390d14bc3ea2d7882ff0f108e2802ad9b043336c5fa637e93581d9a7fdfc2
437while [ 1 ]; do
438    rm -f /tmp/configure-clocksource.service || true;
439    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/configure-clocksource.service\"
440    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
441    sleep 2s;
442done;
443sudo mv /tmp/configure-clocksource.service /etc/systemd/system/configure-clocksource.service
444sudo chown root:root /etc/systemd/system/configure-clocksource.service
445systemctl daemon-reload
446systemctl enable --now configure-clocksource
447"
448        .to_string()),
449        _ => Err(Error::new(
450            ErrorKind::InvalidInput,
451            format!("os_type '{}' not supported", os_type.as_str()),
452        )),
453    }
454}
455
456pub fn system_limit_bump(os_type: OsType) -> io::Result<String> {
457    match os_type {
458        OsType::Ubuntu2004 | OsType::Ubuntu2204 | OsType::Al2023 => Ok("
459###########################
460# bumping up system limits
461# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
462
463echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
464echo fs.inotify.max_user_instances=8192 | sudo tee -a /etc/sysctl.conf
465echo vm.max_map_count=524288 | sudo tee -a /etc/sysctl.conf
466
467# e.g.,
468# \"Accept error: accept tcp [::]:9650: accept4: too many open files; retrying in 1s\"
469sudo echo \"* hard nofile 1000000\" >> /etc/security/limits.conf
470sudo echo \"* soft nofile 1000000\" >> /etc/security/limits.conf
471sudo sysctl -w fs.file-max=1000000
472sudo sysctl -p
473"
474        .to_string()),
475        _ => Err(Error::new(
476            ErrorKind::InvalidInput,
477            format!("os_type '{}' not supported", os_type.as_str()),
478        )),
479    }
480}
481
482pub fn aws_cli(arch_type: ArchType, os_type: OsType) -> io::Result<String> {
483    match (arch_type, os_type) {
484        (ArchType::Amd64
485            | ArchType::Amd64GpuP4NvidiaTeslaA100
486            | ArchType::Amd64GpuG3NvidiaTeslaM60
487            | ArchType::Amd64GpuG4adRadeon
488            | ArchType::Amd64GpuG5NvidiaA10G, OsType::Ubuntu2004 | OsType::Ubuntu2204) => Ok(
489            "
490###########################
491# install AWS CLI
492# https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html
493
494# 'uname -m' to decide x86_64/aarch64
495while [ 1 ]; do
496    sudo rm -f /tmp/awscli-exe-linux-$(uname -m).zip || true;
497    sudo apt-get update -yq && sudo apt-get install -yq wget unzip && wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue https://awscli.amazonaws.com/awscli-exe-linux-$(uname -m).zip
498    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
499    sleep 2s;
500done;
501
502# 'uname -m' to decide x86_64/aarch64
503unzip /tmp/awscli-exe-linux-$(uname -m).zip && sudo ./aws/install
504/usr/local/bin/aws --version
505
506# /usr/local/bin/aws
507which aws
508
509# AWS CLI SSM session manager
510# https://docs.aws.amazon.com/systems-manager/latest/userguide/install-plugin-debian.html
511# 'uname -m' to decide x86_64/aarch64
512curl https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_64bit/session-manager-plugin.deb -o /tmp/session-manager-plugin.deb
513sudo dpkg -i /tmp/session-manager-plugin.deb
514rm -f /tmp/session-manager-plugin.deb
515".to_string()),
516
517    (ArchType::Arm64, OsType::Ubuntu2004 | OsType::Ubuntu2204) => Ok(
518    "
519###########################
520# install AWS CLI
521# https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html
522
523# 'uname -m' to decide x86_64/aarch64
524while [ 1 ]; do
525sudo rm -f /tmp/awscli-exe-linux-$(uname -m).zip || true;
526sudo apt-get update -yq && sudo apt-get install -yq wget unzip && wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue https://awscli.amazonaws.com/awscli-exe-linux-$(uname -m).zip
527if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
528sleep 2s;
529done;
530
531# 'uname -m' to decide x86_64/aarch64
532unzip /tmp/awscli-exe-linux-$(uname -m).zip && sudo ./aws/install
533/usr/local/bin/aws --version
534
535# /usr/local/bin/aws
536which aws
537
538# AWS CLI SSM session manager
539# https://docs.aws.amazon.com/systems-manager/latest/userguide/install-plugin-debian.html
540# 'uname -m' to decide x86_64/aarch64
541curl https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_arm64/session-manager-plugin.deb -o /tmp/session-manager-plugin.deb
542sudo dpkg -i /tmp/session-manager-plugin.deb
543rm -f /tmp/session-manager-plugin.deb
544".to_string()),
545
546        _ => Err(Error::new(
547            ErrorKind::InvalidInput,
548            "os_type not supported",
549        )),
550    }
551}
552
553pub fn ssm_agent(os_type: OsType) -> io::Result<String> {
554    match os_type {
555        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok(
556            "
557###########################
558# install ssm agent
559# https://docs.aws.amazon.com/systems-manager/latest/userguide/agent-install-ubuntu.html
560
561sudo snap install amazon-ssm-agent --classic
562sudo systemctl enable snap.amazon-ssm-agent.amazon-ssm-agent.service
563sudo systemctl restart snap.amazon-ssm-agent.amazon-ssm-agent.service
564mkdir -p /etc/systemd/system/snap.amazon-ssm-agent.amazon-ssm-agent.service.d
565cat > /tmp/amazon-ssm-agent-10-restart-always.conf << EOF
566[Service]
567Restart=always
568RestartSec=60s
569EOF
570
571sudo mkdir -p /etc/systemd/system/snap.amazon-ssm-agent.amazon-ssm-agent.service.d
572sudo mv /tmp/amazon-ssm-agent-10-restart-always.conf /etc/systemd/system/snap.amazon-ssm-agent.amazon-ssm-agent.service.d/10-restart-always.conf
573sudo systemctl start --no-block snap.amazon-ssm-agent.amazon-ssm-agent.service
574".to_string()),
575        _ => Err(Error::new(
576            ErrorKind::InvalidInput,
577            format!("os_type '{}' not supported", os_type.as_str()),
578        )),
579    }
580}
581
582pub fn cloudwatch_agent(os_type: OsType) -> io::Result<String> {
583    match os_type {
584        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok(format!(
585            "
586###########################
587# install cloudwatch agent
588# https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/QuickStartEC2Instance.html
589# 'dpkg --print-architecture' to decide amd64/arm64
590
591while [ 1 ]; do
592    rm -f /tmp/amazon-cloudwatch-agent.deb || true;
593    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/$(dpkg --print-architecture)/latest/amazon-cloudwatch-agent.deb\"
594    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
595    sleep 2s;
596done;
597while [ 1 ]; do
598    sudo dpkg -i -E /tmp/amazon-cloudwatch-agent.deb
599    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
600    sleep 2s;
601done;
602"
603        )),
604        _ => Err(Error::new(
605            ErrorKind::InvalidInput,
606            format!("os_type '{}' not supported", os_type.as_str()),
607        )),
608    }
609}
610
611pub fn static_volume_provisioner(
612    os_type: OsType,
613    id: &str,
614    region: &str,
615    volume_type: &str,
616    volume_size: u32,
617    volume_iops: u32,
618    volume_throughput: u32,
619    ebs_device_name: &str,
620    provisioner_initial_wait_random_seconds: usize,
621) -> io::Result<String> {
622    match os_type {
623        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok(format!(
624            "
625###########################
626# install aws-volume-manager for x86_64 (mac, linux x86), arm64 (M*), aarch64 (graviton)
627# https://github.com/ava-labs/volume-manager/releases
628
629# 'uname -m' to decide x86_64/aarch64
630while [ 1 ]; do
631    rm -f /tmp/aws-volume-provisioner.$(uname -m)-{os_type}-linux-gnu || true;
632    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://github.com/ava-labs/volume-manager/releases/download/latest/aws-volume-provisioner.$(uname -m)-{os_type}-linux-gnu\"
633    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
634    sleep 2s;
635done;
636
637chmod +x /tmp/aws-volume-provisioner.$(uname -m)-{os_type}-linux-gnu
638/tmp/aws-volume-provisioner.$(uname -m)-{os_type}-linux-gnu --version
639
640/tmp/aws-volume-provisioner.$(uname -m)-{os_type}-linux-gnu \\
641--log-level=info \\
642--region {region} \\
643--initial-wait-random-seconds={provisioner_initial_wait_random_seconds} \\
644--id-tag-key=Id \\
645--id-tag-value={id} \\
646--kind-tag-key=Kind \\
647--kind-tag-value=aws-volume-provisioner \\
648--ec2-tag-asg-name-key=ASG_NAME \\
649--asg-tag-key=autoscaling:groupName \\
650--volume-type={volume_type} \\
651--volume-size={volume_size} \\
652--volume-iops={volume_iops} \\
653--volume-throughput={volume_throughput} \\
654--ebs-device-name={ebs_device_name} \\
655--block-device-name=/dev/nvme1n1 \\
656--filesystem-name=ext4 \\
657--mount-directory-path=/data
658
659# set permissions
660sudo chown -R $(whoami) /data
661sudo chown -R ubuntu /data
662",
663            os_type = os_type.as_str(),
664            id=id,
665            region=region,
666            volume_type=volume_type,
667            volume_size=volume_size,
668            volume_iops=volume_iops,
669            volume_throughput=volume_throughput,
670            ebs_device_name=ebs_device_name,
671            provisioner_initial_wait_random_seconds=provisioner_initial_wait_random_seconds,
672        )),
673        _ => Err(Error::new(
674            ErrorKind::InvalidInput,
675            format!("os_type '{}' not supported", os_type.as_str()),
676        )),
677    }
678}
679
680pub fn static_ip_provisioner(
681    os_type: OsType,
682    id: &str,
683    region: &str,
684    provisioner_initial_wait_random_seconds: usize,
685) -> io::Result<String> {
686    match os_type {
687        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok(format!(
688            "
689###########################
690# install aws-ip-manager for x86_64 (mac, linux x86), arm64 (M*), aarch64 (graviton)
691# https://github.com/ava-labs/ip-manager/releases
692
693# 'uname -m' to decide x86_64/aarch64
694while [ 1 ]; do
695    rm -f /tmp/aws-ip-provisioner.$(uname -m)-{os_type}-linux-gnu || true;
696    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://github.com/ava-labs/ip-manager/releases/download/latest/aws-ip-provisioner.$(uname -m)-{os_type}-linux-gnu\"
697    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
698    sleep 2s;
699done;
700
701chmod +x /tmp/aws-ip-provisioner.$(uname -m)-{os_type}-linux-gnu
702/tmp/aws-ip-provisioner.$(uname -m)-{os_type}-linux-gnu --version
703
704/tmp/aws-ip-provisioner.$(uname -m)-{os_type}-linux-gnu \\
705--log-level=info \\
706--region {region} \\
707--initial-wait-random-seconds={provisioner_initial_wait_random_seconds} \\
708--id-tag-key=Id \\
709--id-tag-value={id} \\
710--kind-tag-key=Kind \\
711--kind-tag-value=aws-ip-provisioner \\
712--ec2-tag-asg-name-key=ASG_NAME \\
713--asg-tag-key=autoscaling:groupName \\
714--mounted-eip-file-path=/data/eip.yaml
715",
716            os_type = os_type.as_str(),
717            id=id,
718            region=region,
719            provisioner_initial_wait_random_seconds=provisioner_initial_wait_random_seconds,
720        )),
721        _ => Err(Error::new(
722            ErrorKind::InvalidInput,
723            format!("os_type '{}' not supported", os_type.as_str()),
724        )),
725    }
726}
727
728pub fn anaconda(os_type: OsType) -> io::Result<String> {
729    match os_type {
730        // eval "$(/home/ubuntu/anaconda3/bin/conda shell.bash hook)"
731        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok("
732###########################
733# install anaconda
734# https://docs.conda.io/projects/conda/en/latest/user-guide/install/linux.html
735# https://www.anaconda.com/download#downloads
736
737wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://repo.anaconda.com/archive/Anaconda3-2023.03-1-Linux-$(uname -m).sh\"
738
739# batch mode to not interrupt
740# 'uname -m' to decide x86_64/aarch64
741export PREFIX=/home/ubuntu/anaconda3
742PREFIX=/home/ubuntu/anaconda3 HOME=/home/ubuntu sh /tmp/Anaconda3-2023.03-1-Linux-$(uname -m).sh -b || true
743rm -f /tmp/Anaconda3-2023.03-1-Linux-$(uname -m).sh
744
745# conda update conda -y
746# /home/ubuntu/anaconda3/bin/conda
747
748sudo chown -R ubuntu /home/ubuntu/anaconda3/pkgs || true
749sudo chown -R ubuntu /home/ubuntu/.conda/pkgs || true
750sudo chown -R ubuntu /home/ubuntu/anaconda3/envs || true
751sudo chown -R ubuntu /home/ubuntu/.conda/envs || true
752sudo chown -R ubuntu /home/ubuntu/anaconda3/etc/conda || true
753sudo chown -R ubuntu /home/ubuntu/anaconda3 || true
754
755# check versions
756which conda
757/home/ubuntu/anaconda3/bin/conda --version
758
759# check default system versions
760which python3
761python3 --version
762which python
763python --version
764which pip3
765pip3 --version
766which pip
767pip --version
768
769# check versions from conda
770/home/ubuntu/anaconda3/bin/python3 --version
771/home/ubuntu/anaconda3/bin/python --version
772/home/ubuntu/anaconda3/bin/pip3 --version
773/home/ubuntu/anaconda3/bin/pip --version
774".to_string()),
775        _ => Err(Error::new(
776            ErrorKind::InvalidInput,
777            format!("os_type '{}' not supported", os_type.as_str()),
778        )),
779    }
780}
781
782pub fn python(os_type: OsType) -> io::Result<String> {
783    match os_type {
784        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok("
785###########################
786# install python
787
788sudo apt-get install -yq python3-pip
789sudo apt install -yq python-is-python3
790
791# /usr/bin/python3
792which python3
793python3 --version
794
795# /usr/bin/python
796which python
797python --version
798
799pip3 install --upgrade pip
800
801# /usr/local/bin/pip3
802which pip3
803pip3 --version
804
805# /usr/local/bin/pip
806which pip
807pip --version
808"
809        .to_string()),
810        _ => Err(Error::new(
811            ErrorKind::InvalidInput,
812            format!("os_type '{}' not supported", os_type.as_str()),
813        )),
814    }
815}
816
817pub fn rust(os_type: OsType) -> io::Result<String> {
818    match os_type {
819        OsType::Ubuntu2004 | OsType::Ubuntu2204 => {
820            Ok("
821###########################
822# install rust
823# https://www.rust-lang.org/tools/install
824
825export RUSTUP_HOME=/opt/rust
826export CARGO_HOME=/opt/rust
827sudo mkdir -p /opt/rust
828sudo chown -R ubuntu /opt/rust || true
829sudo curl --proto '=https' --tlsv1.2 -sSf --retry 70 --retry-delay 1 https://sh.rustup.rs | bash -s -- -y --no-modify-path --default-toolchain stable --profile default
830sudo -H -u ubuntu bash -c 'source /opt/rust/env && rustup default stable'
831
832. /opt/rust/env
833
834# /opt/rust/bin/rustc
835which rustc
836rustc --version
837".to_string())
838        }
839
840        OsType::Al2023 => {
841            Ok("
842###########################
843# install rust
844# https://www.rust-lang.org/tools/install
845
846export RUSTUP_HOME=/opt/rust
847export CARGO_HOME=/opt/rust
848curl --proto '=https' --tlsv1.2 -sSf --retry 70 --retry-delay 1 https://sh.rustup.rs | bash -s -- -y --no-modify-path --default-toolchain stable --profile default
849sudo -H -u ec2-user bash -c 'source /opt/rust/env && rustup default stable'
850
851. /opt/rust/env
852
853# /opt/rust/bin/rustc
854which rustc
855rustc --version
856".to_string())
857        }
858
859        _  => {
860            Err(Error::new(
861                ErrorKind::InvalidInput,
862                format!("os_type '{}' not supported", os_type.as_str()),
863            ))
864        }
865    }
866}
867
868pub fn go(os_type: OsType) -> io::Result<String> {
869    match os_type {
870        OsType::Ubuntu2004 | OsType::Ubuntu2204 => {
871            Ok("
872###########################
873# install go for amd64 or arm64
874# https://go.dev/dl
875# 'dpkg --print-architecture' to decide amd64/arm64
876
877# sudo rm -rf /usr/local/go
878# sudo curl -s --retry 70 --retry-delay 1 https://storage.googleapis.com/golang/go1.20.7.linux-$(dpkg --print-architecture).tar.gz | sudo tar -C /usr/local/ -xz
879wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://go.dev/dl/go1.20.7.linux-$(dpkg --print-architecture).tar.gz\"
880rm -rf /usr/local/go && tar -C /usr/local -xzf /tmp/go1.20.7.linux-$(dpkg --print-architecture).tar.gz
881
882/usr/local/go/bin/go version
883go version || true
884".to_string())
885        }
886        _ => Err(Error::new(
887            ErrorKind::InvalidInput,
888            format!("os_type '{}' not supported", os_type.as_str()),
889        )),
890    }
891}
892
893pub fn docker(os_type: OsType) -> io::Result<String> {
894    match os_type {
895        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok(
896            "
897###########################
898# install docker
899# 'dpkg --print-architecture' to decide amd64/arm64
900# 'lsb_release -cs' to decide jammy/focal/*
901
902if ! command -v lsb_release &> /dev/null
903then
904    echo \"lsb_release could not be found\"
905    exit 1
906fi
907
908while [ 1 ]; do
909    sudo apt-get install -yq ca-certificates gnupg
910    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
911    sleep 2s;
912done;
913while [ 1 ]; do
914    sudo rm -f /usr/share/keyrings/docker-archive-keyring.gpg && curl -fsSL --retry 70 --retry-delay 1 https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
915    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
916    sleep 2s;
917done;
918echo \"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \\
919    $(lsb_release -cs) stable\" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
920while [ 1 ]; do
921    sudo apt-get update -y && sudo apt-get install -yq docker-ce docker-ce-cli containerd.io docker-compose-plugin
922    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
923    sleep 2s;
924done;
925
926sudo systemctl enable docker
927
928sudo usermod -aG docker ubuntu
929
930sudo newgrp docker
931sudo systemctl start docker.service
932sudo systemctl enable --now docker
933sudo docker ps
934sudo docker version
935
936# /usr/bin/containerd
937which containerd
938containerd --version
939
940# /usr/bin/ctr
941which ctr
942ctr --version || true
943ctr version || true
944
945# /usr/bin/docker
946which docker
947docker ps
948docker version
949".to_string()),
950        _ => Err(Error::new(
951            ErrorKind::InvalidInput,
952            format!("os_type '{}' not supported", os_type.as_str()),
953        )),
954    }
955}
956
957pub fn containerd(os_type: OsType) -> io::Result<String> {
958    match os_type {
959        OsType::Ubuntu2004 | OsType::Ubuntu2204 => {
960            Ok("
961###########################
962# install containerd
963# https://containerd.io/downloads/
964# 'dpkg --print-architecture' to decide amd64/arm64
965
966# /usr/bin/containerd
967which containerd || true
968containerd --version || true
969
970while [ 1 ]; do
971    export CONTAINERD_CURRENT_VERSION=$(curl -Ls --retry 70 --retry-delay 1 https://api.github.com/repos/containerd/containerd/releases/latest | grep 'tag_name' | cut -d'v' -f2 | cut -d'\"' -f1)
972    rm -f /tmp/containerd-${CONTAINERD_CURRENT_VERSION}-linux-$(dpkg --print-architecture).tar.gz || true;
973    rm -rf /tmp/containerd || true;
974    mkdir -p /tmp/containerd
975    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://github.com/containerd/containerd/releases/download/v${CONTAINERD_CURRENT_VERSION}/containerd-${CONTAINERD_CURRENT_VERSION}-linux-$(dpkg --print-architecture).tar.gz\" -O - | tar -xzv -C /tmp/containerd
976    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
977    sleep 2s;
978done;
979
980chmod +x /tmp/containerd/bin/*
981sudo mv /tmp/containerd/bin/* /usr/bin/
982rm -rf /tmp/containerd
983
984# /usr/bin/containerd
985which containerd
986containerd --version
987
988# /usr/bin/ctr
989which ctr
990ctr --version || true
991ctr version || true
992".to_string())
993        }
994        _  => {
995            Err(Error::new(
996                ErrorKind::InvalidInput,
997                format!("os_type '{}' not supported", os_type.as_str()),
998            ))
999        }
1000    }
1001}
1002
1003pub fn runc(os_type: OsType) -> io::Result<String> {
1004    match os_type {
1005        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok("
1006###########################
1007# install runc
1008# https://github.com/opencontainers/runc
1009# 'dpkg --print-architecture' to decide amd64/arm64
1010
1011which runc || true
1012runc --version || true
1013
1014# this removes \"containerd.io docker-ce\"
1015# sudo apt-get install -yq runc
1016
1017sudo apt-get install -yq libseccomp-dev
1018
1019# rm -rf /tmp/runc
1020# git clone https://github.com/opencontainers/runc /tmp/runc
1021# cd /tmp/runc
1022# make
1023# sudo make install
1024
1025while [ 1 ]; do
1026    export RUNC_CURRENT_VERSION=$(curl -Ls --retry 70 --retry-delay 1 https://api.github.com/repos/opencontainers/runc/releases/latest | grep 'tag_name' | cut -d'v' -f2 | cut -d'\"' -f1)
1027    rm -f /tmp/runc.$(dpkg --print-architecture) || true;
1028    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://github.com/opencontainers/runc/releases/download/v${RUNC_CURRENT_VERSION}/runc.$(dpkg --print-architecture)\"
1029    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1030    sleep 2s;
1031done;
1032
1033chmod +x /tmp/runc.$(dpkg --print-architecture)
1034sudo mv /tmp/runc.$(dpkg --print-architecture) /usr/bin/runc
1035
1036# /usr/bin/runc
1037which runc
1038runc --version
1039"
1040        .to_string()),
1041        _ => Err(Error::new(
1042            ErrorKind::InvalidInput,
1043            format!("os_type '{}' not supported", os_type.as_str()),
1044        )),
1045    }
1046}
1047
1048pub fn cni_plugins(os_type: OsType) -> io::Result<String> {
1049    match os_type {
1050        OsType::Ubuntu2004 | OsType::Ubuntu2204 | OsType::Al2023 => Ok("
1051###########################
1052# install CNI plugins
1053# https://github.com/containernetworking/plugins
1054# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
1055# 'dpkg --print-architecture' to decide amd64/arm64
1056
1057while [ 1 ]; do
1058    export CNI_PLUGIN_CURRENT_VERSION=$(curl -Ls --retry 70 --retry-delay 1 https://api.github.com/repos/containernetworking/plugins/releases/latest | grep 'tag_name' | cut -d'v' -f2 | cut -d'\"' -f1)
1059    rm -f /tmp/cni-plugins-linux-$(dpkg --print-architecture)-v${CNI_PLUGIN_CURRENT_VERSION}.tgz || true;
1060    rm -rf /tmp/cni-plugins-linux-$(dpkg --print-architecture)-v${CNI_PLUGIN_CURRENT_VERSION} || true;
1061    rm -rf /tmp/cni-plugins || true;
1062    mkdir -p /tmp/cni-plugins
1063    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://github.com/containernetworking/plugins/releases/download/v${CNI_PLUGIN_CURRENT_VERSION}/cni-plugins-linux-$(dpkg --print-architecture)-v${CNI_PLUGIN_CURRENT_VERSION}.tgz\" -O - | tar -xzv -C /tmp/cni-plugins
1064    rm -f /tmp/cni-plugins-linux-$(dpkg --print-architecture)-v${CNI_PLUGIN_CURRENT_VERSION}.tgz || true;
1065    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1066    sleep 2s;
1067done;
1068
1069ls -lah /tmp/cni-plugins
1070chmod +x /tmp/cni-plugins/*
1071
1072sudo mkdir -p /opt/cni/bin
1073sudo mv /tmp/cni-plugins/* /opt/cni/bin/
1074rm -rf /tmp/cni-plugins
1075
1076sudo find /opt/cni/bin/
1077"
1078        .to_string()),
1079        _ => Err(Error::new(
1080            ErrorKind::InvalidInput,
1081            format!("os_type '{}' not supported", os_type.as_str()),
1082        )),
1083    }
1084}
1085
1086pub fn protobuf_compiler(os_type: OsType) -> io::Result<String> {
1087    match os_type {
1088        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok("
1089###########################
1090# install protobuf-compiler
1091
1092sudo apt-get install -yq protobuf-compiler
1093"
1094        .to_string()),
1095        _ => Err(Error::new(
1096            ErrorKind::InvalidInput,
1097            format!("os_type '{}' not supported", os_type.as_str()),
1098        )),
1099    }
1100}
1101
1102pub fn aws_cfn_helper(os_type: OsType, python_pip_bin_path: &str) -> io::Result<String> {
1103    match os_type {
1104        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok(format!("
1105###########################
1106# install aws-cfn-bootstrap and other helpers
1107# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-helper-scripts-reference.html
1108# https://repost.aws/knowledge-center/install-cloudformation-scripts
1109
1110# install for root
1111while [ 1 ]; do
1112    pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz
1113    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1114    sleep 2s;
1115done;
1116
1117# pip3 install --user aws-cfn-bootstrap doesn't work
1118# pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz
1119# install for user
1120while [ 1 ]; do
1121    sudo -H -u ubuntu bash -c '{python_pip_bin_path}/pip3 install --user https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz'
1122    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1123    sleep 2s;
1124done;
1125
1126# /home/ubuntu/.local/bin/cfn-hup
1127which cfn-hup
1128cfn-hup --help
1129
1130# sudo /sbin/service cfn-hup restart
1131# sudo ln -s /home/ubuntu/.local/bin/cfn-hup /etc/init.d/cfn-hup
1132# update-rc.d cfn-hup defaults
1133#
1134# sudo systemctl daemon-reload
1135# sudo systemctl status cfn-hup
1136"
1137        )),
1138        _ => Err(Error::new(
1139            ErrorKind::InvalidInput,
1140            format!("os_type '{}' not supported", os_type.as_str()),
1141        )),
1142    }
1143}
1144
1145pub fn saml2aws(os_type: OsType) -> io::Result<String> {
1146    match os_type {
1147        OsType::Ubuntu2004 | OsType::Ubuntu2204 => {
1148            Ok("
1149###########################
1150# install saml2aws
1151# https://api.github.com/repos/Versent/saml2aws/releases/latest
1152# 'dpkg --print-architecture' to decide amd64/arm64
1153
1154while [ 1 ]; do
1155    export SAML2AWS_CURRENT_VERSION=$(curl -Ls --retry 70 --retry-delay 1 https://api.github.com/repos/Versent/saml2aws/releases/latest | grep 'tag_name' | cut -d'v' -f2 | cut -d'\"' -f1)
1156    rm -f /tmp/saml2aws_${SAML2AWS_CURRENT_VERSION}_linux_$(dpkg --print-architecture).tar.gz || true;
1157    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://github.com/Versent/saml2aws/releases/download/v${SAML2AWS_CURRENT_VERSION}/saml2aws_${SAML2AWS_CURRENT_VERSION}_linux_$(dpkg --print-architecture).tar.gz\" -O - | tar -xzv -C /tmp
1158    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1159    sleep 2s;
1160done;
1161
1162chmod +x /tmp/saml2aws
1163sudo mv /tmp/saml2aws /usr/bin/saml2aws
1164
1165saml2aws --version
1166".to_string())
1167        }
1168        _  => {
1169            Err(Error::new(
1170                ErrorKind::InvalidInput,
1171                format!("os_type '{}' not supported", os_type.as_str()),
1172            ))
1173        }
1174    }
1175}
1176
1177pub fn aws_iam_authenticator(os_type: OsType) -> io::Result<String> {
1178    match os_type {
1179        OsType::Ubuntu2004 | OsType::Ubuntu2204 | OsType::Al2023 => {
1180            Ok("
1181###########################
1182# install aws-iam-authenticator
1183# https://docs.aws.amazon.com/eks/latest/userguide/install-aws-iam-authenticator.html
1184# 'dpkg --print-architecture' to decide amd64/arm64
1185
1186while [ 1 ]; do
1187    export AWS_IAM_AUTHENTICATOR_CURRENT_VERSION=$(curl -Ls --retry 70 --retry-delay 1 https://api.github.com/repos/kubernetes-sigs/aws-iam-authenticator/releases/latest | grep 'tag_name' | cut -d'v' -f2 | cut -d'\"' -f1)
1188    rm -f /tmp/aws-iam-authenticator_${AWS_IAM_AUTHENTICATOR_CURRENT_VERSION}_linux_$(dpkg --print-architecture) || true;
1189    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://github.com/kubernetes-sigs/aws-iam-authenticator/releases/download/v${AWS_IAM_AUTHENTICATOR_CURRENT_VERSION}/aws-iam-authenticator_${AWS_IAM_AUTHENTICATOR_CURRENT_VERSION}_linux_$(dpkg --print-architecture)\"
1190    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1191    sleep 2s;
1192done;
1193
1194chmod +x /tmp/aws-iam-authenticator_${AWS_IAM_AUTHENTICATOR_CURRENT_VERSION}_linux_$(dpkg --print-architecture)
1195sudo mv /tmp/aws-iam-authenticator_${AWS_IAM_AUTHENTICATOR_CURRENT_VERSION}_linux_$(dpkg --print-architecture) /usr/bin/aws-iam-authenticator
1196
1197# /usr/bin/aws-iam-authenticator
1198which aws-iam-authenticator
1199aws-iam-authenticator version
1200".to_string())
1201        }
1202        _  => {
1203            Err(Error::new(
1204                ErrorKind::InvalidInput,
1205                format!("os_type '{}' not supported", os_type.as_str()),
1206            ))
1207        }
1208    }
1209}
1210
1211pub fn ecr_credential_helper(os_type: OsType) -> io::Result<String> {
1212    match os_type {
1213        OsType::Ubuntu2004 | OsType::Ubuntu2204 | OsType::Al2023 => Ok("
1214###########################
1215# install ECR credential helper
1216# https://github.com/awslabs/amazon-ecr-credential-helper
1217
1218which docker-credential-ecr-login || true
1219docker-credential-ecr-login version || true
1220
1221while [ 1 ]; do
1222    export ECR_CREDENTIAL_HELPER_CURRENT_VERSION=$(curl -Ls --retry 70 --retry-delay 1 https://api.github.com/repos/awslabs/amazon-ecr-credential-helper/releases/latest | grep 'tag_name' | cut -d'v' -f2 | cut -d'\"' -f1)
1223    rm -f /tmp/aws-iam-authenticator_${ECR_CREDENTIAL_HELPER_CURRENT_VERSION}_linux_$(dpkg --print-architecture) || true;
1224    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://amazon-ecr-credential-helper-releases.s3.us-east-2.amazonaws.com/${ECR_CREDENTIAL_HELPER_CURRENT_VERSION}/linux-$(dpkg --print-architecture)/docker-credential-ecr-login\"
1225    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1226    sleep 2s;
1227done;
1228
1229chmod +x /tmp/docker-credential-ecr-login
1230sudo mv /tmp/docker-credential-ecr-login /usr/bin/docker-credential-ecr-login
1231
1232# /usr/bin/docker-credential-ecr-login
1233which docker-credential-ecr-login
1234docker-credential-ecr-login version
1235"
1236        .to_string()),
1237        _ => Err(Error::new(
1238            ErrorKind::InvalidInput,
1239            format!("os_type '{}' not supported", os_type.as_str()),
1240        )),
1241    }
1242}
1243
1244pub fn ecr_credential_provider(os_type: OsType) -> io::Result<String> {
1245    match os_type {
1246        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok("
1247###########################
1248# install ecr-credential-provider
1249# https://github.com/kubernetes/cloud-provider-aws/tree/master/cmd/ecr-credential-provider
1250# https://github.com/kubernetes/cloud-provider-aws/releases
1251# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
1252
1253if ! command -v go &> /dev/null
1254then
1255    echo \"go could not be found\"
1256    exit 1
1257fi
1258
1259while [ 1 ]; do
1260    HOME=/home/ubuntu GOPATH=/home/ubuntu/go /usr/local/go/bin/go install -v k8s.io/cloud-provider-aws/cmd/ecr-credential-provider@v1.27.1
1261    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1262    sleep 2s;
1263done;
1264
1265which ecr-credential-provider
1266chmod +x /home/ubuntu/go/bin/ecr-credential-provider
1267sudo cp -v /home/ubuntu/go/bin/ecr-credential-provider /usr/bin/ecr-credential-provider
1268
1269# /usr/bin/ecr-credential-provider
1270which ecr-credential-provider
1271
1272# TODO: this blocks
1273# ecr-credential-provider --help
1274
1275sudo mkdir -p /etc/eks
1276sudo mkdir -p /etc/eks/image-credential-provider
1277
1278sudo cp -v /home/ubuntu/go/bin/ecr-credential-provider /etc/eks/image-credential-provider/ecr-credential-provider
1279
1280while [ 1 ]; do
1281    rm -f /tmp/ecr-credential-provider-config.json || true;
1282    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/ecr-credential-provider-config.json\"
1283    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1284    sleep 2s;
1285done;
1286
1287chmod +x /tmp/ecr-credential-provider-config.json
1288sudo mv /tmp/ecr-credential-provider-config.json /etc/eks/image-credential-provider/config.json
1289
1290sudo chown -R root:root /etc/eks
1291sudo chown -R ubuntu:ubuntu /etc/eks
1292find /etc/eks
1293"
1294        .to_string()),
1295        _ => Err(Error::new(
1296            ErrorKind::InvalidInput,
1297            format!("os_type '{}' not supported", os_type.as_str()),
1298        )),
1299    }
1300}
1301
1302pub fn kubelet(os_type: OsType) -> io::Result<String> {
1303    match os_type {
1304        OsType::Ubuntu2004 | OsType::Ubuntu2204 | OsType::Al2023 => {
1305            Ok("
1306###########################
1307# install kubelet
1308# https://kubernetes.io/releases/
1309# https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/
1310# 'dpkg --print-architecture' to decide amd64/arm64
1311
1312while [ 1 ]; do
1313    export K8S_CURRENT_VERSION=$(curl -L -s --retry 70 --retry-delay 1 https://dl.k8s.io/release/stable.txt)
1314    # overwrite with 1.26
1315    export K8S_CURRENT_VERSION=\"v1.26.7\"
1316
1317    rm -f /tmp/kubelet || true;
1318    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://dl.k8s.io/release/${K8S_CURRENT_VERSION}/bin/linux/$(dpkg --print-architecture)/kubelet\"
1319    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1320    sleep 2s;
1321done;
1322
1323chmod +x /tmp/kubelet
1324sudo mv /tmp/kubelet /usr/bin/kubelet
1325rm -f /tmp/kubelet
1326
1327# /usr/bin/kubelet
1328which kubelet
1329kubelet --version
1330".to_string())
1331        }
1332        _  => {
1333            Err(Error::new(
1334                ErrorKind::InvalidInput,
1335                format!("os_type '{}' not supported", os_type.as_str()),
1336            ))
1337        }
1338    }
1339}
1340
1341pub fn kubectl(os_type: OsType) -> io::Result<String> {
1342    match os_type {
1343        OsType::Ubuntu2004 | OsType::Ubuntu2204 => {
1344            Ok("
1345###########################
1346# install kubectl
1347# https://kubernetes.io/releases/
1348# https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/
1349# 'dpkg --print-architecture' to decide amd64/arm64
1350
1351while [ 1 ]; do
1352    export K8S_CURRENT_VERSION=$(curl -L -s --retry 70 --retry-delay 1 https://dl.k8s.io/release/stable.txt)
1353    # overwrite with 1.26
1354    export K8S_CURRENT_VERSION=\"v1.26.7\"
1355
1356    rm -f /tmp/kubectl || true;
1357    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://dl.k8s.io/release/${K8S_CURRENT_VERSION}/bin/linux/$(dpkg --print-architecture)/kubectl\"
1358    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1359    sleep 2s;
1360done;
1361
1362chmod +x /tmp/kubectl
1363sudo mv /tmp/kubectl /usr/bin/kubectl
1364rm -f /tmp/kubectl
1365
1366# /usr/bin/kubectl
1367which kubectl
1368kubectl version --client=true
1369".to_string())
1370        }
1371        _  => {
1372            Err(Error::new(
1373                ErrorKind::InvalidInput,
1374                format!("os_type '{}' not supported", os_type.as_str()),
1375            ))
1376        }
1377    }
1378}
1379
1380pub fn helm(os_type: OsType) -> io::Result<String> {
1381    match os_type {
1382        OsType::Ubuntu2004 | OsType::Ubuntu2204 => {
1383            Ok("
1384###########################
1385# install helm
1386# https://helm.sh/docs/intro/install/
1387# 'dpkg --print-architecture' to decide amd64/arm64
1388
1389while [ 1 ]; do
1390    export HELM_CURRENT_VERSION=$(curl -Ls --retry 70 --retry-delay 1 https://api.github.com/repos/helm/helm/releases/latest | grep 'tag_name' | cut -d'v' -f2 | cut -d'\"' -f1)
1391    rm -f /tmp/helm-${HELM_CURRENT_VERSION}-linux-$(dpkg --print-architecture).tar.gz || true;
1392    rm -rf /tmp/helm || true;
1393    mkdir -p /tmp/helm
1394    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://get.helm.sh/helm-v${HELM_CURRENT_VERSION}-linux-$(dpkg --print-architecture).tar.gz\" -O - | tar -xzv -C /tmp/helm
1395    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1396    sleep 2s;
1397done;
1398
1399chmod +x /tmp/helm/linux-$(dpkg --print-architecture)/helm
1400sudo mv /tmp/helm/linux-$(dpkg --print-architecture)/helm /usr/bin/helm
1401rm -rf /tmp/helm
1402
1403# /usr/bin/helm
1404which helm
1405helm version
1406".to_string())
1407        }
1408        _  => {
1409            Err(Error::new(
1410                ErrorKind::InvalidInput,
1411                format!("os_type '{}' not supported", os_type.as_str()),
1412            ))
1413        }
1414    }
1415}
1416
1417pub fn terraform(os_type: OsType) -> io::Result<String> {
1418    match os_type {
1419        OsType::Ubuntu2004 | OsType::Ubuntu2204 => {
1420            Ok("
1421###########################
1422# install terraform
1423# https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli
1424# 'dpkg --print-architecture' to decide amd64/arm64
1425# 'lsb_release -cs' to decide jammy/focal/*
1426
1427if ! command -v lsb_release &> /dev/null
1428then
1429    echo \"lsb_release could not be found\"
1430    exit 1
1431fi
1432
1433while [ 1 ]; do
1434    sudo apt-get install -yq gnupg software-properties-common
1435    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1436    sleep 2s;
1437done;
1438while [ 1 ]; do
1439    sudo rm -f /usr/share/keyrings/hashicorp-archive-keyring.gpg && curl -fsSL --retry 70 --retry-delay 1 https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
1440    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1441    sleep 2s;
1442done;
1443sudo gpg --no-default-keyring --keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg --fingerprint
1444
1445echo \"deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main\" | sudo tee /etc/apt/sources.list.d/hashicorp.list > /dev/null
1446while [ 1 ]; do
1447    sudo apt-get update -y && sudo apt-get install -yq terraform
1448    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1449    sleep 2s;
1450done;
1451
1452# /usr/bin/terraform
1453which terraform
1454terraform --version
1455".to_string())
1456        }
1457        _  => {
1458            Err(Error::new(
1459                ErrorKind::InvalidInput,
1460                format!("os_type '{}' not supported", os_type.as_str()),
1461            ))
1462        }
1463    }
1464}
1465
1466pub fn ssh_key_with_email(os_type: OsType, email: &str) -> io::Result<String> {
1467    match os_type {
1468        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok(format!(
1469            "
1470###########################
1471# create an SSH key
1472
1473# NOTE/SECURITY: this must be deleted when building AMI
1474ssh-keygen -q -t rsa -b 4096 -C \"{email}\" -N '' -f /home/ubuntu/.ssh/id_rsa <<<y >/dev/null 2>&1
1475eval \"$(ssh-agent -s)\"
1476ssh-add /home/ubuntu/.ssh/id_rsa
1477cat /home/ubuntu/.ssh/id_rsa.pub
1478
1479# set permissions
1480sudo chown -R $(whoami) /home/ubuntu/.ssh
1481sudo chown -R ubuntu /home/ubuntu/.ssh
1482",
1483            email = email,
1484        )),
1485        _ => Err(Error::new(
1486            ErrorKind::InvalidInput,
1487            format!("os_type '{}' not supported", os_type.as_str()),
1488        )),
1489    }
1490}
1491
1492/// ref. <https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/enhanced-networking-ena.html#ena-requirements>
1493/// ref. <https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/enhanced-networking-ena.html>
1494/// ref. <https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/enhanced-networking-ena.html#enhanced-networking-ena-ubuntu>
1495pub fn ena(os_type: OsType) -> io::Result<String> {
1496    match  &os_type {
1497        OsType::Ubuntu2004 |  OsType::Ubuntu2204 => Ok("
1498###########################
1499# enable enhanced networking
1500# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/enhanced-networking-ena.html#enhanced-networking-ena-ubuntu
1501
1502while [ 1 ]; do
1503    sudo apt-get update && sudo apt-get upgrade -y linux-aws
1504    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1505    sleep 2s;
1506done;
1507
1508if ! command -v imds &> /dev/null
1509then
1510    echo \"imds could not be found\"
1511    exit 1
1512fi
1513
1514if ! command -v aws &> /dev/null
1515then
1516    echo \"aws could not be found\"
1517    exit 1
1518fi
1519
1520ip link show
1521
1522# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/enhanced-networking-ena.html#test-enhanced-networking-ena
1523# TODO: this may not work... need pre-installed AMI or restart
1524modinfo ena || true
1525
1526# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html
1527INSTANCE_ID=$(imds /latest/meta-data/instance-id)
1528REGION=$(imds /latest/dynamic/instance-identity/document | jq .region -r)
1529
1530# must stop the instance first
1531# aws ec2 stop-instances --region ${REGION} --instance-ids ${INSTANCE_ID}
1532#
1533# run this outside of EC2
1534# An error occurred (IncorrectInstanceState) when calling the ModifyInstanceAttribute operation: The instance 'i-05f974ac421d49bc2' is not in the 'stopped' state.
1535aws ec2 modify-instance-attribute --region ${REGION} --instance-id ${INSTANCE_ID} --ena-support || true
1536
1537# expects [ true ]
1538aws ec2 describe-instances --region ${REGION} --instance-ids ${INSTANCE_ID} --query \"Reservations[].Instances[].EnaSupport\"
1539
1540# https://docs.aws.amazon.com/cli/latest/reference/ec2/register-image.html
1541# aws ec2 create-image --region ${REGION} --instance-id ${INSTANCE_ID} --name random-ami-name
1542# aws ec2 register-image --region ${REGION} --ena-support --name random-ami-name
1543
1544# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/enhanced-networking-ena.html#test-enhanced-networking-ena
1545# TODO: this may not work... need pre-installed AMI or restart
1546ip link show
1547ethtool -i ens5 || true
1548modinfo ena
1549"
1550        .to_string()),
1551
1552        _ => Err(Error::new(
1553            ErrorKind::InvalidInput,
1554            format!("os_type '{}' not supported",  os_type.as_str()),
1555        )),
1556    }
1557}
1558
1559pub fn nvidia_driver(arch_type: ArchType, os_type: OsType) -> io::Result<String> {
1560    match (&arch_type, &os_type) {
1561        (ArchType::Amd64GpuG3NvidiaTeslaM60 | ArchType::Amd64GpuG4dnNvidiaT4 | ArchType::Amd64GpuG5NvidiaA10G , OsType::Ubuntu2004) => Ok("
1562###########################
1563# install nvidia driver for ubuntu 20.04
1564# https://www.nvidia.com/Download/index.aspx?lang=en-us
1565# https://docs.nvidia.com/datacenter/tesla/tesla-installation-notes/index.html
1566# https://www.nvidia.com/en-us/drivers/unix/
1567
1568# Release Date:	2021.10.26
1569DRIVER_VERSION=460.106.00
1570BASE_URL=https://us.download.nvidia.com/tesla
1571
1572while [ 1 ]; do
1573    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"${BASE_URL}/${DRIVER_VERSION}/NVIDIA-Linux-$(uname -m)-${DRIVER_VERSION}.run\"
1574    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1575    sleep 2s;
1576done;
1577
1578# 'uname -m' to decide x86_64/aarch64
1579sudo sh /tmp/NVIDIA-Linux-$(uname -m)-${DRIVER_VERSION}.run --silent --ui=none --no-questions
1580rm -f /tmp/NVIDIA-Linux-$(uname -m)-${DRIVER_VERSION}.run
1581sudo tail /var/log/nvidia-installer.log
1582
1583# check the driver
1584find /usr/lib/modules -name nvidia.ko
1585find /usr/lib/modules -name nvidia.ko -exec modinfo {} \\;
1586
1587if ! command -v nvidia-smi &> /dev/null
1588then
1589    echo \"nvidia-smi could not be found\"
1590    exit 1
1591fi
1592
1593# /usr/bin/nvidia-smi
1594which nvidia-smi
1595nvidia-smi
1596"
1597        .to_string()),
1598
1599        (ArchType::Amd64GpuG3NvidiaTeslaM60 | ArchType::Amd64GpuG4dnNvidiaT4 | ArchType::Amd64GpuG5NvidiaA10G , OsType::Ubuntu2204) => Ok("
1600###########################
1601# install nvidia driver for ubuntu 22.04
1602# https://www.nvidia.com/Download/index.aspx?lang=en-us
1603# https://docs.nvidia.com/datacenter/tesla/tesla-installation-notes/index.html
1604# https://www.nvidia.com/en-us/drivers/unix/
1605
1606# THIS IS DIFFERENT FOR UBUNTU LAPTOP
1607# e.g.,
1608# Release Date: 2023.8.29
1609# DRIVER_VERSION=535.104.05
1610# https://us.download.nvidia.com/XFree86/Linux-x86_64/535.104.05/NVIDIA-Linux-x86_64-535.104.05.run
1611#
1612# in case of rollback
1613# original system76 ubuntu 22.04 ships
1614# DRIVER_VERSION=525.105.17
1615# CUDA_VERSION=12.0
1616
1617# Release Date: 2023.8.29
1618DRIVER_VERSION=535.104.05
1619BASE_URL=https://us.download.nvidia.com/tesla
1620
1621while [ 1 ]; do
1622    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"${BASE_URL}/${DRIVER_VERSION}/NVIDIA-Linux-$(uname -m)-${DRIVER_VERSION}.run\"
1623    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1624    sleep 2s;
1625done;
1626
1627# 'uname -m' to decide x86_64/aarch64
1628sudo sh /tmp/NVIDIA-Linux-$(uname -m)-${DRIVER_VERSION}.run --silent --ui=none --no-questions
1629rm -f /tmp/NVIDIA-Linux-$(uname -m)-${DRIVER_VERSION}.run
1630tail /var/log/nvidia-installer.log
1631
1632# check the driver
1633find /usr/lib/modules -name nvidia.ko
1634find /usr/lib/modules -name nvidia.ko -exec modinfo {} \\;
1635
1636if ! command -v nvidia-smi &> /dev/null
1637then
1638    echo \"nvidia-smi could not be found\"
1639    exit 1
1640fi
1641
1642# /usr/bin/nvidia-smi
1643which nvidia-smi
1644nvidia-smi
1645"
1646        .to_string()),
1647
1648        _ => Err(Error::new(
1649            ErrorKind::InvalidInput,
1650            format!("arch_type '{}', os_type '{}' not supported", arch_type.as_str(), os_type.as_str()),
1651        )),
1652    }
1653}
1654
1655pub fn nvidia_cuda_toolkit(os_type: OsType) -> io::Result<String> {
1656    match os_type {
1657        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok("
1658###########################
1659# install nvidia cuda toolkit
1660# this installs cuda 11 by default on ubuntu 20.04
1661# sudo apt install -yq nvidia-cuda-toolkit
1662# https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=x86_64&Distribution=Ubuntu&target_version=20.04&target_type=runfile_local
1663# https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=x86_64&Distribution=Ubuntu&target_version=22.04&target_type=runfile_local
1664
1665# /cuda-installer: error while loading shared libraries: libxml2.so.2: cannot open shared object file: No such file or directory
1666sudo apt-get install -y libxml2
1667
1668# this upgrades to CUDA Version: 12.2
1669CUDA_VERSION=12.2.2
1670TOOL_KIT_VERSION=535.104.05
1671BASE_URL=https://developer.download.nvidia.com/compute/cuda
1672
1673# add --override-driver-check to overwrite
1674while [ 1 ]; do
1675    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"${BASE_URL}/${CUDA_VERSION}/local_installers/cuda_${CUDA_VERSION}_${TOOL_KIT_VERSION}_linux.run\"
1676    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1677    sleep 2s;
1678done;
1679
1680sudo sh /tmp/cuda_${CUDA_VERSION}_${TOOL_KIT_VERSION}_linux.run --silent
1681rm -f /tmp/cuda_${CUDA_VERSION}_${TOOL_KIT_VERSION}_linux.run
1682tail /var/log/cuda-installer.log
1683
1684if ! command -v nvcc &> /dev/null
1685then
1686    echo \"nvcc could not be found\"
1687
1688    # PATH env might not been updated yet
1689    # exit 1
1690fi
1691
1692# /usr/local/cuda-12.2/bin
1693which nvcc || true
1694nvcc --version || true
1695/usr/local/cuda-12.2/bin/nvcc --version
1696
1697if ! command -v nvidia-smi &> /dev/null
1698then
1699    echo \"nvidia-smi could not be found\"
1700    exit 1
1701fi
1702
1703# /usr/bin/nvidia-smi
1704which nvidia-smi
1705nvidia-smi
1706"
1707            .to_string()),
1708
1709        _ => Err(Error::new(
1710            ErrorKind::InvalidInput,
1711            format!("os_type '{}' not supported", os_type.as_str()),
1712        )),
1713    }
1714}
1715
1716pub fn nvidia_container_toolkit(os_type: OsType) -> io::Result<String> {
1717    match os_type {
1718        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok("
1719###########################
1720# install nvidia container toolkit
1721# https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html
1722
1723distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
1724curl -fsSL --retry 70 --retry-delay 1 https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
1725
1726curl -s -L --retry 70 --retry-delay 1 https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \\
1727sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \\
1728sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
1729
1730sudo apt-get update
1731sudo apt-get install -yq nvidia-container-toolkit
1732
1733if ! command -v nvidia-ctk &> /dev/null
1734then
1735    echo \"nvidia-ctk could not be found\"
1736    exit 1
1737fi
1738
1739# /usr/bin/nvidia-ctk
1740which nvidia-ctk
1741nvidia-ctk --version
1742
1743if command -v docker &> /dev/null
1744then
1745    echo \"checking nvidia container toolkit with docker\"
1746    # TODO: support other runtime?
1747    sudo nvidia-ctk runtime configure --runtime=docker
1748
1749    # https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/getting-started.html#install-nvidia-gpu-operator
1750    cat /etc/nvidia-container-runtime/config.toml
1751
1752    # restart docker
1753    sudo systemctl restart docker
1754
1755    # test nvidia container toolkit
1756    sudo docker run --rm --runtime=nvidia --gpus all nvidia/cuda:11.6.2-base-ubuntu20.04 nvidia-smi
1757else
1758    echo \"docker not install -- skip checking 'nvidia-ctk runtime configure'\"
1759fi
1760"
1761            .to_string()),
1762
1763        _ => Err(Error::new(
1764            ErrorKind::InvalidInput,
1765            format!("os_type '{}' not supported", os_type.as_str()),
1766        )),
1767    }
1768}
1769
1770pub fn amd_radeon_gpu_driver(arch_type: ArchType, os_type: OsType) -> io::Result<String> {
1771    match (&arch_type, &os_type) {
1772        (ArchType::Amd64GpuG4adRadeon, OsType::Ubuntu2004 | OsType::Ubuntu2204) => Ok("
1773###########################
1774# install AMD Radeon driver for ubuntu
1775# https://www.amd.com/en/support/kb/faq/amdgpupro-install
1776# https://amdgpu-install.readthedocs.io/en/latest/install-prereq.html#downloading-the-installer-package
1777# https://amdgpu-install.readthedocs.io/en/latest/install-script.html
1778
1779while [ 1 ]; do
1780    sudo apt-get -y install linux-modules-extra-aws
1781    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1782    sleep 2s;
1783done;
1784
1785DRIVER_VERSION1=5.4
1786DRIVER_VERSION2=5.4.50403-1_all
1787BASE_URL=https://repo.radeon.com
1788
1789while [ 1 ]; do
1790    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"${BASE_URL}/${DRIVER_VERSION1}/ubuntu/$(lsb_release -cs)/amdgpu-install_${DRIVER_VERSION2}.deb\"
1791    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1792    sleep 2s;
1793done;
1794
1795# 'uname -m' to decide x86_64/aarch64
1796sudo apt-get -y install /tmp/amdgpu-install_${DRIVER_VERSION2}.deb
1797sudo sh /tmp/NVIDIA-Linux-$(uname -m)-${DRIVER_VERSION}.run --silent --ui=none --no-questions
1798
1799if ! command -v amdgpu-install &> /dev/null
1800then
1801    echo \"amdgpu-install could not be found\"
1802    exit 1
1803fi
1804
1805# https://amdgpu-install.readthedocs.io/en/latest/install-script.html
1806sudo amdgpu-install -y --accept-eula --usecase=dkms
1807
1808# /usr/bin/amdgpu-install
1809which amdgpu-install
1810amdgpu-install -h
1811"
1812        .to_string()),
1813
1814        _ => Err(Error::new(
1815            ErrorKind::InvalidInput,
1816            format!("arch_type '{}', os_type '{}' not supported", arch_type.as_str(), os_type.as_str()),
1817        )),
1818    }
1819}
1820
1821pub fn cmake(os_type: OsType, python_pip_bin_path: &str) -> io::Result<String> {
1822    match os_type {
1823        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok(format!("
1824###########################
1825# install cmake
1826# https://askubuntu.com/questions/355565/how-do-i-install-the-latest-version-of-cmake-from-the-command-line
1827
1828sudo apt purge --auto-remove cmake
1829
1830# wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | sudo tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null
1831# sudo apt-add-repository 'deb https://apt.kitware.com/ubuntu/ focal main'
1832# sudo apt-add-repository 'deb https://apt.kitware.com/ubuntu/ jammy main'
1833# sudo apt update -y
1834# sudo apt install -yq cmake
1835
1836which pip
1837{python_pip_bin_path}/pip install --upgrade cmake
1838
1839which cmake
1840cmake --version
1841")),
1842        _ => Err(Error::new(
1843            ErrorKind::InvalidInput,
1844            format!("os_type '{}' not supported", os_type.as_str()),
1845        )),
1846    }
1847}
1848
1849pub fn dev_bark(
1850    os_type: OsType,
1851    python_pip_bin_path: &str,
1852    data_volume_mounted: bool,
1853) -> io::Result<String> {
1854    match os_type {
1855        OsType::Ubuntu2004 | OsType::Ubuntu2204 => {
1856            let clone_dir = if data_volume_mounted {
1857                String::from("/data")
1858            } else {
1859                String::from("/home/ubuntu")
1860            };
1861            Ok(format!(
1862                "
1863###########################
1864# install bark
1865# https://github.com/suno-ai/bark
1866
1867ls -lah {clone_dir}/
1868git clone https://github.com/suno-ai/bark.git {clone_dir}/bark
1869cd {clone_dir}/bark
1870
1871which python
1872{python_pip_bin_path}/python -m pip install .
1873which pip
1874{python_pip_bin_path}/pip install --verbose nltk
1875"
1876            ))
1877        }
1878        _ => Err(Error::new(
1879            ErrorKind::InvalidInput,
1880            format!("os_type '{}' not supported", os_type.as_str()),
1881        )),
1882    }
1883}
1884
1885pub fn gcc7(os_type: OsType) -> io::Result<String> {
1886    match os_type {
1887        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok("
1888###########################
1889# install gcc7
1890# gcc downgrade <8, otherwise faiss 'cmake -B build .' fails with
1891# 138 | #error -- unsupported GNU version! gcc versions later than 8 are not supported!
1892# https://stackoverflow.com/questions/65605972/cmake-unsupported-gnu-version-gcc-versions-later-than-8-are-not-supported
1893
1894sudo apt remove -y gcc
1895sudo apt-get install gcc-7 g++-7 -y
1896sudo ln -s /usr/bin/gcc-7 /usr/bin/gcc
1897sudo ln -s /usr/bin/g++-7 /usr/bin/g++
1898sudo ln -s /usr/bin/gcc-7 /usr/bin/cc
1899sudo ln -s /usr/bin/g++-7 /usr/bin/c++
1900/usr/bin/gcc --version
1901/usr/bin/c++ -v
1902"
1903            .to_string()),
1904        _ => Err(Error::new(
1905            ErrorKind::InvalidInput,
1906            format!("os_type '{}' not supported", os_type.as_str()),
1907        )),
1908    }
1909}
1910
1911pub fn dev_faiss_gpu(os_type: OsType, data_volume_mounted: bool) -> io::Result<String> {
1912    match os_type {
1913        OsType::Ubuntu2004 | OsType::Ubuntu2204 => {
1914            let clone_dir = if data_volume_mounted {
1915                String::from("/data")
1916            } else {
1917                String::from("/home/ubuntu")
1918            };
1919            Ok(format!("
1920###########################
1921# install faiss
1922# https://github.com/facebookresearch/faiss#installing
1923# https://github.com/facebookresearch/faiss/blob/main/INSTALL.md
1924# https://github.com/facebookresearch/faiss/wiki/Troubleshooting
1925
1926# otherwise,
1927# Could NOT find BLAS (missing: BLAS_LIBRARIES)
1928sudo apt-get install -yq libopenblas-dev
1929
1930# otherwise,
1931# Could NOT find SWIG (missing: SWIG_EXECUTABLE SWIG_DIR python)
1932sudo apt-get install -yq swig
1933
1934/usr/bin/gcc --version
1935/usr/bin/c++ -v
1936
1937which cmake
1938cmake --version
1939
1940ls -lah {clone_dir}/
1941git clone https://github.com/facebookresearch/faiss.git {clone_dir}/faiss
1942
1943# generates the system-dependent configuration/build files in the build/ subdirectory
1944# cd {clone_dir}/faiss
1945# cmake -B build .
1946
1947# builds the C++ library
1948# cd {clone_dir}/faiss
1949# make -C build -j faiss
1950
1951# builds the python bindings for Faiss
1952# cd {clone_dir}/faiss
1953# make -C build -j swigfaiss
1954
1955# generates and installs the python package
1956# cd {clone_dir}/faiss/build/faiss/python
1957# python setup.py install
1958
1959# make the compiled library (either libfaiss.a or libfaiss.so on Linux) available system-wide, as well as the C++ headers
1960# cd {clone_dir}/faiss
1961# make -C build install
1962"
1963        ))
1964        }
1965        _ => Err(Error::new(
1966            ErrorKind::InvalidInput,
1967            format!("os_type '{}' not supported", os_type.as_str()),
1968        )),
1969    }
1970}
1971
1972pub fn eks_worker_node_ami_scratch(os_type: OsType) -> io::Result<String> {
1973    match os_type {
1974        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok("
1975###########################
1976# install EKS worker node AMI (from scratch)
1977# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
1978
1979#######
1980# install packages
1981# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
1982#######
1983while [ 1 ]; do
1984    sudo apt-get update -yq
1985    sudo apt-get upgrade -yq
1986    sudo apt-get install -yq conntrack socat nfs-kernel-server ipvsadm
1987    sudo apt-get clean
1988    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
1989    sleep 2s;
1990done;
1991
1992#######
1993### Stuff required by \"protectKernelDefaults=true\"
1994# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
1995#######
1996cat << EOF | sudo tee -a /etc/sysctl.d/99-amazon.conf
1997vm.overcommit_memory=1
1998kernel.panic=10
1999kernel.panic_on_oops=1
2000EOF
2001
2002#######
2003# set up nvidia-smi check scripts
2004# https://github.com/awslabs/amazon-eks-ami/blob/master/files/bootstrap.sh
2005#######
2006# https://stackoverflow.com/questions/27920806/how-to-avoid-heredoc-expanding-variables
2007cat << 'EOF' > /tmp/check-nvidia-smi.sh
2008#!/usr/bin/env bash
2009
2010if command -v nvidia-smi &> /dev/null; then
2011    echo \"INFO: nvidia-smi found\"
2012
2013    nvidia-smi -q > /tmp/nvidia-smi-check
2014    if [[ \"$?\" == \"0\" ]]; then
2015        sudo nvidia-smi -pm 1 # set persistence mode
2016        sudo nvidia-smi --auto-boost-default=0
2017
2018        GPUNAME=$(nvidia-smi -L | head -n1)
2019        echo \"INFO: GPU name: $GPUNAME\"
2020
2021        # set application clock to maximum
2022        if [[ $GPUNAME == *\"A100\"* ]]; then
2023            nvidia-smi -ac 1215,1410
2024        elif [[ $GPUNAME == *\"V100\"* ]]; then
2025            nvidia-smi -ac 877,1530
2026        elif [[ $GPUNAME == *\"K80\"* ]]; then
2027            nvidia-smi -ac 2505,875
2028        elif [[ $GPUNAME == *\"T4\"* ]]; then
2029            nvidia-smi -ac 5001,1590
2030        elif [[ $GPUNAME == *\"M60\"* ]]; then
2031            nvidia-smi -ac 2505,1177
2032        else
2033            echo \"WARN: unsupported GPU\"
2034        fi
2035    else
2036        echo \"ERROR: nvidia-smi check failed!\"
2037        cat /tmp/nvidia-smi-check
2038    fi
2039else
2040    echo \"INFO: nvidia-smi NOT found\"
2041fi
2042EOF
2043cat /tmp/check-nvidia-smi.sh
2044chmod +x /tmp/check-nvidia-smi.sh
2045sudo mv /tmp/check-nvidia-smi.sh /etc/check-nvidia-smi.sh
2046
2047#######
2048# set up files
2049# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
2050#######
2051sudo mkdir -p /etc/eks
2052sudo chown -R root:root /etc/eks
2053
2054targets=(
2055    get-ecr-uri.sh
2056    eni-max-pods.txt
2057    bootstrap.sh
2058    max-pods-calculator.sh
2059)
2060for target in \"${targets[@]}\"
2061do
2062    while [ 1 ]; do
2063        rm -f /tmp/${target} || true;
2064        wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/${target}\"
2065        if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
2066        sleep 2s;
2067    done;
2068    chmod +x /tmp/${target}
2069    sudo mv /tmp/${target} /etc/eks/${target}
2070done
2071
2072sudo chown -R root:root /etc/eks
2073sudo chown -R ubuntu:ubuntu /etc/eks
2074find /etc/eks
2075
2076#######
2077# set up iptables
2078# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
2079#######
2080sudo mkdir -p /etc/eks
2081sudo chown -R root:root /etc/eks
2082
2083while [ 1 ]; do
2084    rm -f /tmp/iptables-restore.service || true;
2085    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/iptables-restore.service\"
2086    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
2087    sleep 2s;
2088done;
2089chmod +x /tmp/iptables-restore.service
2090sudo mv /tmp/iptables-restore.service /etc/eks/iptables-restore.service
2091
2092sudo chown -R root:root /etc/eks
2093sudo chown -R ubuntu:ubuntu /etc/eks
2094find /etc/eks
2095
2096#######
2097# set up containerd
2098# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
2099#######
2100sudo mkdir -p /etc/eks
2101sudo chown -R root:root /etc/eks
2102sudo mkdir -p /etc/eks/containerd
2103
2104targets=(
2105    containerd-config.toml
2106    kubelet-containerd.service
2107    sandbox-image.service
2108    pull-sandbox-image.sh
2109    pull-image.sh
2110)
2111for target in \"${targets[@]}\"
2112do
2113    while [ 1 ]; do
2114        rm -f /tmp/${target} || true;
2115        wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/${target}\"
2116        if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
2117        sleep 2s;
2118    done;
2119    chmod +x /tmp/${target}
2120    sudo mv /tmp/${target} /etc/eks/containerd/${target}
2121done
2122
2123sudo chown -R root:root /etc/eks
2124sudo chown -R ubuntu:ubuntu /etc/eks
2125find /etc/eks
2126
2127sudo mkdir -p /etc/systemd/system/containerd.service.d
2128cat << EOF | sudo tee /etc/systemd/system/containerd.service.d/10-compat-symlink.conf
2129[Service]
2130ExecStartPre=/bin/ln -sf /run/containerd/containerd.sock /run/dockershim.sock
2131EOF
2132
2133cat << EOF | sudo tee -a /etc/modules-load.d/containerd.conf
2134overlay
2135br_netfilter
2136EOF
2137
2138cat << EOF | sudo tee -a /etc/sysctl.d/99-kubernetes-cri.conf
2139net.bridge.bridge-nf-call-ip6tables = 1
2140net.bridge.bridge-nf-call-iptables = 1
2141net.ipv4.ip_forward = 1
2142EOF
2143
2144cat << EOF | sudo tee /etc/systemd/system/containerd.service
2145[Unit]
2146Description=containerd
2147Documentation=https://containerd.io
2148
2149[Service]
2150Type=notify
2151ExecStart=/usr/bin/containerd
2152
2153[Install]
2154WantedBy=multi-user.target
2155EOF
2156
2157sudo systemctl enable containerd
2158sudo systemctl restart containerd
2159sudo ctr version || true
2160
2161#######
2162# set up log-collector for EKS
2163# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
2164#######
2165sudo mkdir -p /etc/eks
2166sudo chown -R root:root /etc/eks
2167sudo mkdir -p /etc/eks/log-collector-script/
2168
2169while [ 1 ]; do
2170    rm -f /tmp/eks-log-collector.sh || true;
2171    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/log-collector-script/linux/eks-log-collector.sh\"
2172    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
2173    sleep 2s;
2174done;
2175chmod +x /tmp/eks-log-collector.sh
2176sudo mv /tmp/eks-log-collector.sh /etc/eks/log-collector-script/eks-log-collector.sh
2177
2178sudo chown -R root:root /etc/eks
2179sudo chown -R ubuntu:ubuntu /etc/eks
2180find /etc/eks
2181
2182#######
2183# set up logrotate for kube-proxy
2184# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
2185#######
2186sudo mkdir -p /var/log/journal
2187
2188while [ 1 ]; do
2189    rm -f /tmp/logrotate-kube-proxy || true;
2190    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/logrotate-kube-proxy\"
2191    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
2192    sleep 2s;
2193done;
2194sudo mv /tmp/logrotate-kube-proxy /etc/logrotate.d/kube-proxy
2195sudo chown root:root /etc/logrotate.d/kube-proxy
2196
2197while [ 1 ]; do
2198    rm -f /tmp/logrotate.conf || true;
2199    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/logrotate.conf\"
2200    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
2201    sleep 2s;
2202done;
2203sudo mv /tmp/logrotate.conf /etc/logrotate.conf
2204sudo chown root:root /etc/logrotate.conf
2205
2206#######
2207# set up kubernetes
2208# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
2209#######
2210sudo mkdir -p /var/lib/kubernetes
2211sudo mkdir -p /var/lib/kubelet
2212sudo mkdir -p /etc/kubernetes
2213sudo mkdir -p /etc/kubernetes/manifests
2214sudo mkdir -p /etc/kubernetes/kubelet
2215sudo mkdir -p /etc/systemd/system/kubelet.service.d
2216
2217while [ 1 ]; do
2218    rm -f /tmp/kubelet-kubeconfig || true;
2219    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/kubelet-kubeconfig\"
2220    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
2221    sleep 2s;
2222done;
2223sudo mv /tmp/kubelet-kubeconfig /var/lib/kubelet/kubeconfig
2224sudo chown root:root /var/lib/kubelet/kubeconfig
2225
2226while [ 1 ]; do
2227    rm -f /tmp/kubelet.service || true;
2228    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/kubelet.service\"
2229    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
2230    sleep 2s;
2231done;
2232sudo mv /tmp/kubelet.service /etc/systemd/system/kubelet.service
2233sudo chown root:root /etc/systemd/system/kubelet.service
2234
2235while [ 1 ]; do
2236    rm -f /tmp/kubelet-config.json || true;
2237    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/kubelet-config.json\"
2238    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
2239    sleep 2s;
2240done;
2241sudo mv /tmp/kubelet-config.json /etc/kubernetes/kubelet/kubelet-config.json
2242sudo chown root:root /etc/kubernetes/kubelet/kubelet-config.json
2243
2244sudo systemctl daemon-reload
2245sudo systemctl disable kubelet
2246
2247#######
2248# cache images
2249# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
2250#######
2251ISOLATED_REGIONS=${ISOLATED_REGIONS:-us-iso-east-1 us-iso-west-1 us-isob-east-1}
2252EKS_CACHE_CONTAINER_IMAGES=${EKS_CACHE_CONTAINER_IMAGES:-true}
2253BINARY_BUCKET_REGION=${BINARY_BUCKET_REGION:-us-west-2}
2254PAUSE_CONTAINER_VERSION=${PAUSE_CONTAINER_VERSION:-3.5}
2255KUBERNETES_VERSION=${KUBERNETES_VERSION:-1.26}
2256
2257if [[ \"$EKS_CACHE_CONTAINER_IMAGES\" == \"true\" ]] && ! [[ ${ISOLATED_REGIONS} =~ $BINARY_BUCKET_REGION ]]; then
2258    AWS_DOMAIN=$(imds 'latest/meta-data/services/domain')
2259    ECR_URI=$(/etc/eks/get-ecr-uri.sh \"${BINARY_BUCKET_REGION}\" \"${AWS_DOMAIN}\")
2260
2261    PAUSE_CONTAINER=\"${ECR_URI}/eks/pause:${PAUSE_CONTAINER_VERSION}\"
2262    echo \"PAUSE_CONTAINER: ${PAUSE_CONTAINER}\"
2263
2264    cat /etc/eks/containerd/containerd-config.toml | sed s,SANDBOX_IMAGE,$PAUSE_CONTAINER,g | sudo tee /etc/eks/containerd/containerd-cached-pause-config.toml
2265
2266    sudo mkdir -p /etc/containerd
2267    sudo chown -R root:root /etc/containerd
2268    sudo cp -v /etc/eks/containerd/containerd-cached-pause-config.toml /etc/containerd/config.toml
2269    sudo cp -v /etc/eks/containerd/sandbox-image.service /etc/systemd/system/sandbox-image.service
2270    sudo chown root:root /etc/systemd/system/sandbox-image.service
2271
2272    sudo systemctl daemon-reload
2273    sudo systemctl restart containerd
2274    sudo systemctl enable containerd sandbox-image
2275
2276    # e.g., 1.26
2277    K8S_MINOR_VERSION=$(echo \"${KUBERNETES_VERSION}\" | cut -d'.' -f1-2)
2278    echo \"K8S_MINOR_VERSION: ${K8S_MINOR_VERSION}\"
2279
2280    #### Cache kube-proxy images starting with the addon default version and the latest version
2281    KUBE_PROXY_ADDON_VERSIONS=$(aws eks describe-addon-versions --addon-name kube-proxy --kubernetes-version=${K8S_MINOR_VERSION})
2282    echo \"KUBE_PROXY_ADDON_VERSIONS: ${KUBE_PROXY_ADDON_VERSIONS}\"
2283
2284    KUBE_PROXY_IMGS=()
2285    if [[ $(jq '.addons | length' <<< $KUBE_PROXY_ADDON_VERSIONS) -gt 0 ]]; then
2286        DEFAULT_KUBE_PROXY_FULL_VERSION=$(echo \"${KUBE_PROXY_ADDON_VERSIONS}\" | jq -r '.addons[] .addonVersions[] | select(.compatibilities[] .defaultVersion==true).addonVersion')
2287        DEFAULT_KUBE_PROXY_VERSION=$(echo \"${DEFAULT_KUBE_PROXY_FULL_VERSION}\" | cut -d\"-\" -f1)
2288        DEFAULT_KUBE_PROXY_PLATFORM_VERSION=$(echo \"${DEFAULT_KUBE_PROXY_FULL_VERSION}\" | cut -d\"-\" -f2)
2289
2290        LATEST_KUBE_PROXY_FULL_VERSION=$(echo \"${KUBE_PROXY_ADDON_VERSIONS}\" | jq -r '.addons[] .addonVersions[] .addonVersion' | sort -V | tail -n1)
2291        LATEST_KUBE_PROXY_VERSION=$(echo \"${LATEST_KUBE_PROXY_FULL_VERSION}\" | cut -d\"-\" -f1)
2292        LATEST_KUBE_PROXY_PLATFORM_VERSION=$(echo \"${LATEST_KUBE_PROXY_FULL_VERSION}\" | cut -d\"-\" -f2)
2293
2294        KUBE_PROXY_IMGS=(
2295            ## Default kube-proxy images
2296            \"${ECR_URI}/eks/kube-proxy:${DEFAULT_KUBE_PROXY_VERSION}-${DEFAULT_KUBE_PROXY_PLATFORM_VERSION}\"
2297            \"${ECR_URI}/eks/kube-proxy:${DEFAULT_KUBE_PROXY_VERSION}-minimal-${DEFAULT_KUBE_PROXY_PLATFORM_VERSION}\"
2298
2299            ## Latest kube-proxy images
2300            \"${ECR_URI}/eks/kube-proxy:${LATEST_KUBE_PROXY_VERSION}-${LATEST_KUBE_PROXY_PLATFORM_VERSION}\"
2301            \"${ECR_URI}/eks/kube-proxy:${LATEST_KUBE_PROXY_VERSION}-minimal-${LATEST_KUBE_PROXY_PLATFORM_VERSION}\"
2302        )
2303    fi
2304    echo \"KUBE_PROXY_IMGS: ${KUBE_PROXY_IMGS}\"
2305
2306    #### Cache VPC CNI images starting with the addon default version and the latest version
2307    VPC_CNI_ADDON_VERSIONS=$(aws eks describe-addon-versions --addon-name vpc-cni --kubernetes-version=${K8S_MINOR_VERSION})
2308    echo \"VPC_CNI_ADDON_VERSIONS: ${VPC_CNI_ADDON_VERSIONS}\"
2309
2310    VPC_CNI_IMGS=()
2311    if [[ $(jq '.addons | length' <<< $VPC_CNI_ADDON_VERSIONS) -gt 0 ]]; then
2312        DEFAULT_VPC_CNI_VERSION=$(echo \"${VPC_CNI_ADDON_VERSIONS}\" | jq -r '.addons[] .addonVersions[] | select(.compatibilities[] .defaultVersion==true).addonVersion')
2313        LATEST_VPC_CNI_VERSION=$(echo \"${VPC_CNI_ADDON_VERSIONS}\" | jq -r '.addons[] .addonVersions[] .addonVersion' | sort -V | tail -n1)
2314        CNI_IMG=\"${ECR_URI}/amazon-k8s-cni\"
2315        CNI_INIT_IMG=\"${CNI_IMG}-init\"
2316
2317        VPC_CNI_IMGS=(
2318            ## Default VPC CNI Images
2319            \"${CNI_IMG}:${DEFAULT_VPC_CNI_VERSION}\"
2320            \"${CNI_INIT_IMG}:${DEFAULT_VPC_CNI_VERSION}\"
2321
2322            ## Latest VPC CNI Images
2323            \"${CNI_IMG}:${LATEST_VPC_CNI_VERSION}\"
2324            \"${CNI_INIT_IMG}:${LATEST_VPC_CNI_VERSION}\"
2325        )
2326    fi
2327    echo \"VPC_CNI_IMGS: ${VPC_CNI_IMGS}\"
2328
2329    CACHE_IMGS=(
2330        \"${PAUSE_CONTAINER}\"
2331        ${KUBE_PROXY_IMGS[@]+\"${KUBE_PROXY_IMGS[@]}\"}
2332        ${VPC_CNI_IMGS[@]+\"${VPC_CNI_IMGS[@]}\"}
2333    )
2334    echo \"CACHE_IMGS: ${CACHE_IMGS}\"
2335
2336    PULLED_IMGS=()
2337    for img in \"${CACHE_IMGS[@]}\"; do
2338        ## only kube-proxy-minimal is vended for K8s 1.24+
2339        if [[ \"${img}\" == *\"kube-proxy:\"* ]] && [[ \"${img}\" != *\"-minimal-\"* ]] && vercmp \"${K8S_MINOR_VERSION}\" gteq \"1.24\"; then
2340            continue
2341        fi
2342
2343        ## Since eksbuild.x version may not match the image tag, we need to decrement the eksbuild version until we find the latest image tag within the app semver
2344        eksbuild_version=\"1\"
2345        if [[ ${img} == *'eksbuild.'* ]]; then
2346            eksbuild_version=$(echo \"${img}\" | grep -o 'eksbuild\\.[0-9]\\+' | cut -d'.' -f2)
2347        fi
2348
2349        ## iterate through decrementing the build version each time
2350        for build_version in $(seq \"${eksbuild_version}\" -1 1); do
2351            img=$(echo \"${img}\" | sed -E \"s/eksbuild.[0-9]+/eksbuild.${build_version}/\")
2352            echo \"ctr pulling/fetching an image ${img}\"
2353            if /etc/eks/containerd/pull-image.sh \"${img}\"; then
2354                PULLED_IMGS+=(\"${img}\")
2355                break
2356            elif [[ \"${build_version}\" -eq 1 ]]; then
2357                exit 1
2358            fi
2359        done
2360    done
2361    echo \"PULLED_IMGS: ${PULLED_IMGS}\"
2362
2363    #### Tag the pulled down image for all other regions in the partition
2364    for region in $(aws ec2 describe-regions --all-regions | jq -r '.Regions[] .RegionName'); do
2365        for img in \"${PULLED_IMGS[@]}\"; do
2366            regional_img=\"${img/$BINARY_BUCKET_REGION/$region}\"
2367            echo \"tagging a pulled image ${regional_img}\"
2368            sudo ctr -n k8s.io image tag \"${img}\" \"${regional_img}\" || :
2369
2370            ## Tag ECR fips endpoint for supported regions
2371            if [[ \"${region}\" =~ (us-east-1|us-east-2|us-west-1|us-west-2|us-gov-east-1|us-gov-east-2) ]]; then
2372                regional_fips_img=\"${regional_img/.ecr./.ecr-fips.}\"
2373                sudo ctr -n k8s.io image tag \"${img}\" \"${regional_fips_img}\" || :
2374                sudo ctr -n k8s.io image tag \"${img}\" \"${regional_fips_img/-eksbuild.1/}\" || :
2375            fi
2376
2377            ## Cache the non-addon VPC CNI images since \"v*.*.*-eksbuild.1\" is equivalent to leaving off the eksbuild suffix
2378            if [[ \"${img}\" == *\"-cni\"*\"-eksbuild.1\" ]]; then
2379                sudo ctr -n k8s.io image tag \"${img}\" \"${regional_img/-eksbuild.1/}\" || :
2380            fi
2381        done
2382    done
2383fi
2384
2385#######
2386# write release file
2387# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
2388# 'uname -m' to decide x86_64/aarch64
2389#######
2390BASE_AMI_ID=$(imds /latest/meta-data/ami-id)
2391sudo rm -f /tmp/release-full
2392cat << EOF > /tmp/release-full
2393BASE_AMI_ID=$BASE_AMI_ID
2394BUILD_TIME=$(date)
2395BUILD_KERNEL=$(uname -r)
2396ARCH=$(uname -m)
2397
2398OS_RELEASE_ID=$(. /etc/os-release;echo $ID)
2399OS_RELEASE_DISTRIBUTION=$(. /etc/os-release;echo $ID$VERSION_ID)
2400
2401RUNC_VERSION=\"$(runc --version | head -1 | tr -d '\\n')\"
2402CONTAINERD_VERSION=\"$(containerd --version)\"
2403CTR_VERSION=\"$(ctr --version)\"
2404
2405KUBELET_VERSION=\"$(kubelet --version)\"
2406EOF
2407cat /tmp/release-full
2408
2409sudo cp -v /tmp/release-full /etc/release-full
2410sudo chmod 0444 /etc/release-full
2411"
2412        .to_string()),
2413        _ => Err(Error::new(
2414            ErrorKind::InvalidInput,
2415            format!("os_type '{}' not supported", os_type.as_str()),
2416        )),
2417    }
2418}
2419
2420pub fn eks_worker_node_ami_reuse(os_type: OsType) -> io::Result<String> {
2421    match os_type {
2422        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok("
2423###########################
2424# install EKS worker node AMI (minimum)
2425# to build on top of the existing ubuntu AMI
2426# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
2427
2428#######
2429# set up nvidia-smi check scripts
2430# https://github.com/awslabs/amazon-eks-ami/blob/master/files/bootstrap.sh
2431#######
2432# https://stackoverflow.com/questions/27920806/how-to-avoid-heredoc-expanding-variables
2433cat << 'EOF' > /tmp/check-nvidia-smi.sh
2434#!/usr/bin/env bash
2435
2436if command -v nvidia-smi &> /dev/null; then
2437    echo \"INFO: nvidia-smi found\"
2438
2439    nvidia-smi -q > /tmp/nvidia-smi-check
2440    if [[ \"$?\" == \"0\" ]]; then
2441        sudo nvidia-smi -pm 1 # set persistence mode
2442        sudo nvidia-smi --auto-boost-default=0
2443
2444        GPUNAME=$(nvidia-smi -L | head -n1)
2445        echo \"INFO: GPU name: $GPUNAME\"
2446
2447        # set application clock to maximum
2448        if [[ $GPUNAME == *\"A100\"* ]]; then
2449            nvidia-smi -ac 1215,1410
2450        elif [[ $GPUNAME == *\"V100\"* ]]; then
2451            nvidia-smi -ac 877,1530
2452        elif [[ $GPUNAME == *\"K80\"* ]]; then
2453            nvidia-smi -ac 2505,875
2454        elif [[ $GPUNAME == *\"T4\"* ]]; then
2455            nvidia-smi -ac 5001,1590
2456        elif [[ $GPUNAME == *\"M60\"* ]]; then
2457            nvidia-smi -ac 2505,1177
2458        else
2459            echo \"WARN: unsupported GPU\"
2460        fi
2461    else
2462        echo \"ERROR: nvidia-smi check failed!\"
2463        cat /tmp/nvidia-smi-check
2464    fi
2465else
2466    echo \"INFO: nvidia-smi NOT found\"
2467fi
2468EOF
2469cat /tmp/check-nvidia-smi.sh
2470chmod +x /tmp/check-nvidia-smi.sh
2471sudo mv /tmp/check-nvidia-smi.sh /etc/check-nvidia-smi.sh
2472
2473#######
2474# set up files
2475# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
2476#######
2477sudo mkdir -p /etc/eks
2478sudo chown -R root:root /etc/eks
2479sudo mkdir -p /etc/eks/containerd
2480
2481# https://github.com/awslabs/amazon-eks-ami/commit/7c45ddef58bbb50c869095eeb2185e41a745db6f
2482targets=(
2483    containerd-config.toml
2484    eni-max-pods.txt
2485    get-ecr-uri.sh
2486    pull-image.sh
2487    pull-sandbox-image.sh
2488    sandbox-image.service
2489)
2490for target in \"${targets[@]}\"
2491do
2492    while [ 1 ]; do
2493        rm -f /tmp/${target} || true;
2494        wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/${target}\"
2495        if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
2496        sleep 2s;
2497    done;
2498    chmod +x /tmp/${target}
2499    sudo cp -v /tmp/${target} /etc/eks/containerd/${target}
2500    sudo mv /tmp/${target} /etc/eks/${target}
2501done
2502
2503sudo chown -R root:root /etc/eks
2504sudo chown -R ubuntu:ubuntu /etc/eks
2505find /etc/eks
2506
2507sudo ctr version || true
2508
2509#######
2510# set up log-collector for EKS
2511# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
2512#######
2513sudo mkdir -p /etc/eks
2514sudo chown -R root:root /etc/eks
2515sudo mkdir -p /etc/eks/log-collector-script/
2516
2517while [ 1 ]; do
2518    rm -f /tmp/eks-log-collector.sh || true;
2519    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/log-collector-script/linux/eks-log-collector.sh\"
2520    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
2521    sleep 2s;
2522done;
2523chmod +x /tmp/eks-log-collector.sh
2524sudo mv /tmp/eks-log-collector.sh /etc/eks/log-collector-script/eks-log-collector.sh
2525
2526sudo chown -R root:root /etc/eks
2527sudo chown -R ubuntu:ubuntu /etc/eks
2528find /etc/eks
2529
2530#######
2531# set up logrotate for kube-proxy
2532# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
2533#######
2534sudo mkdir -p /var/log/journal
2535
2536while [ 1 ]; do
2537    rm -f /tmp/logrotate-kube-proxy || true;
2538    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/logrotate-kube-proxy\"
2539    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
2540    sleep 2s;
2541done;
2542sudo mv /tmp/logrotate-kube-proxy /etc/logrotate.d/kube-proxy
2543sudo chown root:root /etc/logrotate.d/kube-proxy
2544
2545while [ 1 ]; do
2546    rm -f /tmp/logrotate.conf || true;
2547    wget --quiet --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 --tries=70 --directory-prefix=/tmp/ --continue \"https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/logrotate.conf\"
2548    if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
2549    sleep 2s;
2550done;
2551sudo mv /tmp/logrotate.conf /etc/logrotate.conf
2552sudo chown root:root /etc/logrotate.conf
2553
2554#######
2555# cache images
2556# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
2557#######
2558ISOLATED_REGIONS=${ISOLATED_REGIONS:-us-iso-east-1 us-iso-west-1 us-isob-east-1}
2559EKS_CACHE_CONTAINER_IMAGES=${EKS_CACHE_CONTAINER_IMAGES:-true}
2560BINARY_BUCKET_REGION=${BINARY_BUCKET_REGION:-us-west-2}
2561PAUSE_CONTAINER_VERSION=${PAUSE_CONTAINER_VERSION:-3.5}
2562KUBERNETES_VERSION=${KUBERNETES_VERSION:-1.26}
2563
2564if [[ \"$EKS_CACHE_CONTAINER_IMAGES\" == \"true\" ]] && ! [[ ${ISOLATED_REGIONS} =~ $BINARY_BUCKET_REGION ]]; then
2565    AWS_DOMAIN=$(imds 'latest/meta-data/services/domain')
2566    ECR_URI=$(/etc/eks/get-ecr-uri.sh \"${BINARY_BUCKET_REGION}\" \"${AWS_DOMAIN}\")
2567
2568    PAUSE_CONTAINER=\"${ECR_URI}/eks/pause:${PAUSE_CONTAINER_VERSION}\"
2569    echo \"PAUSE_CONTAINER: ${PAUSE_CONTAINER}\"
2570
2571    cat /etc/eks/containerd/containerd-config.toml | sed s,SANDBOX_IMAGE,$PAUSE_CONTAINER,g | sudo tee /etc/eks/containerd/containerd-cached-pause-config.toml
2572
2573    sudo mkdir -p /etc/containerd
2574    sudo chown -R root:root /etc/containerd
2575    sudo cp -v /etc/eks/containerd/containerd-cached-pause-config.toml /etc/containerd/config.toml
2576    sudo cp -v /etc/eks/containerd/sandbox-image.service /etc/systemd/system/sandbox-image.service
2577    sudo chown root:root /etc/systemd/system/sandbox-image.service
2578
2579    sudo systemctl daemon-reload
2580    sudo systemctl restart containerd
2581    sudo systemctl enable containerd sandbox-image
2582
2583    # e.g., 1.26
2584    K8S_MINOR_VERSION=$(echo \"${KUBERNETES_VERSION}\" | cut -d'.' -f1-2)
2585    echo \"K8S_MINOR_VERSION: ${K8S_MINOR_VERSION}\"
2586
2587    #### Cache kube-proxy images starting with the addon default version and the latest version
2588    KUBE_PROXY_ADDON_VERSIONS=$(aws eks describe-addon-versions --addon-name kube-proxy --kubernetes-version=${K8S_MINOR_VERSION})
2589    echo \"KUBE_PROXY_ADDON_VERSIONS: ${KUBE_PROXY_ADDON_VERSIONS}\"
2590
2591    KUBE_PROXY_IMGS=()
2592    if [[ $(jq '.addons | length' <<< $KUBE_PROXY_ADDON_VERSIONS) -gt 0 ]]; then
2593        DEFAULT_KUBE_PROXY_FULL_VERSION=$(echo \"${KUBE_PROXY_ADDON_VERSIONS}\" | jq -r '.addons[] .addonVersions[] | select(.compatibilities[] .defaultVersion==true).addonVersion')
2594        DEFAULT_KUBE_PROXY_VERSION=$(echo \"${DEFAULT_KUBE_PROXY_FULL_VERSION}\" | cut -d\"-\" -f1)
2595        DEFAULT_KUBE_PROXY_PLATFORM_VERSION=$(echo \"${DEFAULT_KUBE_PROXY_FULL_VERSION}\" | cut -d\"-\" -f2)
2596
2597        LATEST_KUBE_PROXY_FULL_VERSION=$(echo \"${KUBE_PROXY_ADDON_VERSIONS}\" | jq -r '.addons[] .addonVersions[] .addonVersion' | sort -V | tail -n1)
2598        LATEST_KUBE_PROXY_VERSION=$(echo \"${LATEST_KUBE_PROXY_FULL_VERSION}\" | cut -d\"-\" -f1)
2599        LATEST_KUBE_PROXY_PLATFORM_VERSION=$(echo \"${LATEST_KUBE_PROXY_FULL_VERSION}\" | cut -d\"-\" -f2)
2600
2601        KUBE_PROXY_IMGS=(
2602            ## Default kube-proxy images
2603            \"${ECR_URI}/eks/kube-proxy:${DEFAULT_KUBE_PROXY_VERSION}-${DEFAULT_KUBE_PROXY_PLATFORM_VERSION}\"
2604            \"${ECR_URI}/eks/kube-proxy:${DEFAULT_KUBE_PROXY_VERSION}-minimal-${DEFAULT_KUBE_PROXY_PLATFORM_VERSION}\"
2605
2606            ## Latest kube-proxy images
2607            \"${ECR_URI}/eks/kube-proxy:${LATEST_KUBE_PROXY_VERSION}-${LATEST_KUBE_PROXY_PLATFORM_VERSION}\"
2608            \"${ECR_URI}/eks/kube-proxy:${LATEST_KUBE_PROXY_VERSION}-minimal-${LATEST_KUBE_PROXY_PLATFORM_VERSION}\"
2609        )
2610    fi
2611    echo \"KUBE_PROXY_IMGS: ${KUBE_PROXY_IMGS}\"
2612
2613    #### Cache VPC CNI images starting with the addon default version and the latest version
2614    VPC_CNI_ADDON_VERSIONS=$(aws eks describe-addon-versions --addon-name vpc-cni --kubernetes-version=${K8S_MINOR_VERSION})
2615    echo \"VPC_CNI_ADDON_VERSIONS: ${VPC_CNI_ADDON_VERSIONS}\"
2616
2617    VPC_CNI_IMGS=()
2618    if [[ $(jq '.addons | length' <<< $VPC_CNI_ADDON_VERSIONS) -gt 0 ]]; then
2619        DEFAULT_VPC_CNI_VERSION=$(echo \"${VPC_CNI_ADDON_VERSIONS}\" | jq -r '.addons[] .addonVersions[] | select(.compatibilities[] .defaultVersion==true).addonVersion')
2620        LATEST_VPC_CNI_VERSION=$(echo \"${VPC_CNI_ADDON_VERSIONS}\" | jq -r '.addons[] .addonVersions[] .addonVersion' | sort -V | tail -n1)
2621        CNI_IMG=\"${ECR_URI}/amazon-k8s-cni\"
2622        CNI_INIT_IMG=\"${CNI_IMG}-init\"
2623
2624        VPC_CNI_IMGS=(
2625            ## Default VPC CNI Images
2626            \"${CNI_IMG}:${DEFAULT_VPC_CNI_VERSION}\"
2627            \"${CNI_INIT_IMG}:${DEFAULT_VPC_CNI_VERSION}\"
2628
2629            ## Latest VPC CNI Images
2630            \"${CNI_IMG}:${LATEST_VPC_CNI_VERSION}\"
2631            \"${CNI_INIT_IMG}:${LATEST_VPC_CNI_VERSION}\"
2632        )
2633    fi
2634    echo \"VPC_CNI_IMGS: ${VPC_CNI_IMGS}\"
2635
2636    CACHE_IMGS=(
2637        \"${PAUSE_CONTAINER}\"
2638        ${KUBE_PROXY_IMGS[@]+\"${KUBE_PROXY_IMGS[@]}\"}
2639        ${VPC_CNI_IMGS[@]+\"${VPC_CNI_IMGS[@]}\"}
2640    )
2641    echo \"CACHE_IMGS: ${CACHE_IMGS}\"
2642
2643    PULLED_IMGS=()
2644    for img in \"${CACHE_IMGS[@]}\"; do
2645        ## only kube-proxy-minimal is vended for K8s 1.24+
2646        if [[ \"${img}\" == *\"kube-proxy:\"* ]] && [[ \"${img}\" != *\"-minimal-\"* ]] && vercmp \"${K8S_MINOR_VERSION}\" gteq \"1.24\"; then
2647            continue
2648        fi
2649
2650        ## Since eksbuild.x version may not match the image tag, we need to decrement the eksbuild version until we find the latest image tag within the app semver
2651        eksbuild_version=\"1\"
2652        if [[ ${img} == *'eksbuild.'* ]]; then
2653            eksbuild_version=$(echo \"${img}\" | grep -o 'eksbuild\\.[0-9]\\+' | cut -d'.' -f2)
2654        fi
2655
2656        ## iterate through decrementing the build version each time
2657        for build_version in $(seq \"${eksbuild_version}\" -1 1); do
2658            img=$(echo \"${img}\" | sed -E \"s/eksbuild.[0-9]+/eksbuild.${build_version}/\")
2659            echo \"ctr pulling/fetching an image ${img}\"
2660            if /etc/eks/containerd/pull-image.sh \"${img}\"; then
2661                PULLED_IMGS+=(\"${img}\")
2662                break
2663            elif [[ \"${build_version}\" -eq 1 ]]; then
2664                exit 1
2665            fi
2666        done
2667    done
2668    echo \"PULLED_IMGS: ${PULLED_IMGS}\"
2669
2670    #### Tag the pulled down image for all other regions in the partition
2671    for region in $(aws ec2 describe-regions --all-regions | jq -r '.Regions[] .RegionName'); do
2672        for img in \"${PULLED_IMGS[@]}\"; do
2673            regional_img=\"${img/$BINARY_BUCKET_REGION/$region}\"
2674            echo \"tagging a pulled image ${regional_img}\"
2675            sudo ctr -n k8s.io image tag \"${img}\" \"${regional_img}\" || :
2676
2677            ## Tag ECR fips endpoint for supported regions
2678            if [[ \"${region}\" =~ (us-east-1|us-east-2|us-west-1|us-west-2|us-gov-east-1|us-gov-east-2) ]]; then
2679                regional_fips_img=\"${regional_img/.ecr./.ecr-fips.}\"
2680                sudo ctr -n k8s.io image tag \"${img}\" \"${regional_fips_img}\" || :
2681                sudo ctr -n k8s.io image tag \"${img}\" \"${regional_fips_img/-eksbuild.1/}\" || :
2682            fi
2683
2684            ## Cache the non-addon VPC CNI images since \"v*.*.*-eksbuild.1\" is equivalent to leaving off the eksbuild suffix
2685            if [[ \"${img}\" == *\"-cni\"*\"-eksbuild.1\" ]]; then
2686                sudo ctr -n k8s.io image tag \"${img}\" \"${regional_img/-eksbuild.1/}\" || :
2687            fi
2688        done
2689    done
2690fi
2691
2692#######
2693# write release file
2694# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/install-worker.sh
2695# 'uname -m' to decide x86_64/aarch64
2696#######
2697BASE_AMI_ID=$(imds /latest/meta-data/ami-id)
2698sudo rm -f /tmp/release-full
2699cat << EOF > /tmp/release-full
2700BASE_AMI_ID=$BASE_AMI_ID
2701BUILD_TIME=$(date)
2702BUILD_KERNEL=$(uname -r)
2703ARCH=$(uname -m)
2704
2705OS_RELEASE_ID=$(. /etc/os-release;echo $ID)
2706OS_RELEASE_DISTRIBUTION=$(. /etc/os-release;echo $ID$VERSION_ID)
2707
2708RUNC_VERSION=\"$(runc --version | head -1 | tr -d '\\n')\"
2709CONTAINERD_VERSION=\"$(containerd --version)\"
2710CTR_VERSION=\"$(ctr --version)\"
2711
2712KUBELET_VERSION=\"$(kubelet --version)\"
2713EOF
2714cat /tmp/release-full
2715
2716sudo cp -v /tmp/release-full /etc/release-full
2717sudo chmod 0444 /etc/release-full
2718"
2719        .to_string()),
2720        _ => Err(Error::new(
2721            ErrorKind::InvalidInput,
2722            format!("os_type '{}' not supported", os_type.as_str()),
2723        )),
2724    }
2725}
2726
2727pub fn aws_key(
2728    os_type: OsType,
2729    region: &str,
2730    aws_secret_key_id: &str,
2731    aws_secret_access_key: &str,
2732) -> io::Result<String> {
2733    match os_type {
2734        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok(format!(
2735            "
2736###########################
2737# writing AWS access key
2738
2739# NOTE/SECURITY: this must be deleted when building AMI
2740mkdir -p /home/ubuntu/.aws || true
2741rm -f /home/ubuntu/.aws/config || true
2742set +x
2743cat << EOF > /home/ubuntu/.aws/config
2744[default]
2745region = {region}
2746EOF
2747
2748rm -f /home/ubuntu/.aws/credentials || true
2749set +x
2750cat << EOF > /home/ubuntu/.aws/credentials
2751[default]
2752aws_access_key_id = {aws_secret_key_id}
2753aws_secret_access_key = {aws_secret_access_key}
2754EOF
2755
2756set -x
2757aws sts get-caller-identity
2758",
2759        )),
2760        _ => Err(Error::new(
2761            ErrorKind::InvalidInput,
2762            format!("os_type '{}' not supported", os_type.as_str()),
2763        )),
2764    }
2765}
2766
2767pub fn ami_info(os_type: OsType) -> io::Result<String> {
2768    match os_type {
2769        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok("
2770###########################
2771# print/write AMI info
2772# 'uname -m' to decide x86_64/aarch64
2773
2774# sudo find /etc/systemd/system/
2775sudo systemctl daemon-reload
2776sudo systemctl list-units --type=service --no-pager
2777df -h
2778
2779BASE_AMI_ID=$(imds /latest/meta-data/ami-id)
2780sudo rm -f /tmp/release
2781cat << EOF > /tmp/release
2782BASE_AMI_ID=$BASE_AMI_ID
2783BUILD_TIME=$(date)
2784BUILD_KERNEL=$(uname -r)
2785ARCH=$(uname -m)
2786EOF
2787cat /tmp/release
2788
2789sudo cp -v /tmp/release /etc/release
2790sudo chmod 0444 /etc/release
2791"
2792        .to_string()),
2793        _ => Err(Error::new(
2794            ErrorKind::InvalidInput,
2795            format!("os_type '{}' not supported", os_type.as_str()),
2796        )),
2797    }
2798}
2799
2800pub fn cluster_info(s3_bucket: &str, id: &str, data_directory_mounted: bool) -> String {
2801    if data_directory_mounted {
2802        format!(
2803            "
2804###########################
2805# write cluster information, assume /data is mounted
2806
2807echo -n \"{s3_bucket}\" > /data/current_s3_bucket
2808echo -n \"{id}\" > /data/current_id
2809"
2810        )
2811    } else {
2812        format!(
2813            "
2814###########################
2815# write cluster information, assume /data is not mounted
2816
2817echo -n \"{s3_bucket}\" > /tmp/current_s3_bucket
2818echo -n \"{id}\" > /tmp/current_id
2819"
2820        )
2821    }
2822}
2823
2824pub fn cleanup_image_packages(os_type: OsType) -> io::Result<String> {
2825    match os_type {
2826        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok("
2827###########################
2828# clean up packages
2829
2830sudo apt clean
2831sudo apt-get clean
2832"
2833        .to_string()),
2834        _ => Err(Error::new(
2835            ErrorKind::InvalidInput,
2836            format!("os_type '{}' not supported", os_type.as_str()),
2837        )),
2838    }
2839}
2840
2841pub fn cleanup_image_tmp_dir(os_type: OsType) -> io::Result<String> {
2842    match os_type {
2843        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok("
2844###########################
2845# clean up /tmp directory
2846
2847sudo rm -rf /tmp/*
2848"
2849        .to_string()),
2850        _ => Err(Error::new(
2851            ErrorKind::InvalidInput,
2852            format!("os_type '{}' not supported", os_type.as_str()),
2853        )),
2854    }
2855}
2856
2857pub fn cleanup_image_ssh_keys(os_type: OsType) -> io::Result<String> {
2858    match os_type {
2859        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok("
2860###########################
2861# WARN
2862# clean up image (useful/required for AMI builds)
2863# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/cleanup.sh
2864# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/validate.sh
2865
2866# sleep enough time for poller
2867# to grab the successful init completion
2868sleep 120
2869
2870sudo rm -rf \\
2871/etc/ssh/ssh_host* \\
2872/home/ubuntu/.ssh/authorized_keys \\
2873/root/.ssh/authorized_keys
2874"
2875        .to_string()),
2876        _ => Err(Error::new(
2877            ErrorKind::InvalidInput,
2878            format!("os_type '{}' not supported", os_type.as_str()),
2879        )),
2880    }
2881}
2882
2883pub fn cleanup_image_aws_credentials(os_type: OsType) -> io::Result<String> {
2884    match os_type {
2885        OsType::Ubuntu2004 | OsType::Ubuntu2204 => Ok("
2886###########################
2887# WARN
2888# clean up AWS credentials
2889# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/cleanup.sh
2890# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/validate.sh
2891
2892sudo rm -rf /home/ubuntu/.aws
2893"
2894        .to_string()),
2895
2896        OsType::Al2023 => Ok("
2897###########################
2898# WARN
2899# clean up AWS credentials
2900# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/cleanup.sh
2901# https://github.com/awslabs/amazon-eks-ami/blob/master/scripts/validate.sh
2902
2903sudo rm -rf /home/ec2-user/.aws
2904"
2905        .to_string()),
2906
2907        _ => Err(Error::new(
2908            ErrorKind::InvalidInput,
2909            format!("os_type '{}' not supported", os_type.as_str()),
2910        )),
2911    }
2912}