Skip to main content
Glama
rootfs_build.sh9.86 kB
#!/bin/bash # TODO(johnrwatson): In theory we should be able to run this task for any of the # components we need rootfs' for but there are some cyclone-specifics bits that # will cause us problems set -euxo pipefail # TODO(johnrwatson): We need to port this to python or similar, and check for # OS-dependencies that are required. i.e. docker and basic priviledged # escalation for the mounts # i.e. ./${git_metadata} | jq -r '.abbreviated_commit_hash' (it returns a json # blob output via python) git_metadata=$1 # i.e. ./metadata-out.json (a build metadata file containing contents of type # etc) build_metadata_out=$2 # i.e. output the file ./johns_rootfs.tar tar_file_out=$3 # Shift the parsed arguments off after assignment shift 3 # The rest of the inputs are a list of input files or directories, to also # include in the build i.e. consume a binary ./johns_binary.bin for use within # this script binary_inputs=("$@") echo "-------------------------------------" echo "Info: Initiating rootfs build" echo "Artifact Version: $(jq -r '.canonical_version' <"$git_metadata")" echo "Output File: $tar_file_out" echo "Input Binaries (list):" for binary_input in "${binary_inputs[@]}"; do echo "$( echo "$binary_input" \ | awk -F "/" '{print $NF}' ) Full Path: $binary_input" done echo "-------------------------------------" BUCKROOT="$BUCK_SCRATCH_PATH" # This is provided by buck2 PACKAGEDIR=$(realpath "$BUCKROOT/cyclone-pkg") ROOTFS="$PACKAGEDIR/cyclone-rootfs.ext4" ROOTFSMOUNT="$PACKAGEDIR/rootfs" ROOTFS_TAR="rootfs.tar.gz" INITSCRIPT="$PACKAGEDIR/init.sh" ALPINE_VERSION=3.18 # Vendored from https://github.com/fnichol/libsh/blob/main/lib/setup_traps.sh setup_traps() { local _sig for _sig in HUP INT QUIT ALRM TERM; do trap " $1 trap - $_sig EXIT kill -s $_sig "'"$$"' "$_sig" done # shellcheck disable=SC2064 trap "$1" EXIT unset _sig } cleanup() { set +e # cleanup the PACKAGEDIR sudo umount -fv "$ROOTFSMOUNT" rm -rfv "$ROOTFSMOUNT" "$INITSCRIPT" "$ROOTFS_TAR" } setup_traps cleanup # create disk and mount to a known location mkdir -pv "$ROOTFSMOUNT" # As of now, the overlay that maps this image is 10,737,418,240 bytes, # which 10gb. If you make the image below larger than that, you must increase # the size of the overlays. They can be found in # lib/si-firecracker/src/scripts/{firecracker-setup|prepare_jailer}.sh dd if=/dev/zero of="$ROOTFS" bs=1M count=7189 mkfs.ext4 -v "$ROOTFS" sudo mount -v "$ROOTFS" "$ROOTFSMOUNT" cyclone_args=( --bind-vsock 3:52 --lang-server /usr/local/bin/lang-js --limit-requests 1 --watch-timeout 30 --enable-ping --enable-watch --disable-forwarder --enable-process-gatherer -vvvv ) # got get the rootfs tar and unpack it curl "https://dl-cdn.alpinelinux.org/alpine/v$ALPINE_VERSION/releases/$(arch)/alpine-minirootfs-$ALPINE_VERSION.0-$(arch).tar.gz" -o $ROOTFS_TAR sudo tar xf rootfs.tar.gz -C "$ROOTFSMOUNT" #ENTER CHROOT sudo chroot "$ROOTFSMOUNT" sh <<EOL echo "nameserver 8.8.8.8" >"/etc/resolv.conf" apk update && apk add --no-cache \ aws-cli \ binutils \ curl \ git \ mingetty \ openssh \ openssh-client \ openrc \ py3-pip \ python3 \ runuser \ skopeo \ wget \ xz \ gcc \ musl-dev \ python3-dev \ libffi-dev \ openssl-dev \ cargo \ make # Minimal nix setup for dynamic linking mkdir -p /nix && cd /tmp ARCH=\$(uname -m) if [ "\$ARCH" = "aarch64" ]; then NIX_ARCH="aarch64-linux" LD_LINUX_NAME="ld-linux-aarch64.so.1" LIB_DIR="/lib" else NIX_ARCH="x86_64-linux" LD_LINUX_NAME="ld-linux-x86-64.so.2" LIB_DIR="/lib64" fi curl -L https://releases.nixos.org/nix/nix-2.30.2/nix-2.30.2-\$NIX_ARCH.tar.xz | tar -xJ mv nix-2.30.2-\$NIX_ARCH/store /nix/ && cp -rL nix-2.30.2-\$NIX_ARCH/.reginfo /nix/store/ /nix/store/*-nix-*/bin/nix-store --load-db < /nix/store/.reginfo # Linking stuff for cyclone and langjs only GLIBC_PATH=\$(find /nix/store -name "glibc-*" -type d 2>/dev/null | head -1) GCC_LIB_PATH=\$(find /nix/store -name "*gcc*-lib" -type d 2>/dev/null | head -1) LD_LINUX_PATH=\$(find /nix/store -name "\$LD_LINUX_NAME" | head -1) [ -n "\$LD_LINUX_PATH" ] && mkdir -p \$LIB_DIR && ln -sf "\$LD_LINUX_PATH" \$LIB_DIR/\$LD_LINUX_NAME rm -rf nix-2.30.2-\$NIX_ARCH # Install binary tools mkdir -p /usr/local/bin /opt && cd /tmp install_github_binary() { local repo=\$1 binary=\$2 pattern=\$3 local url=\$(curl -s "https://api.github.com/repos/\$repo/releases/latest" | grep "\$pattern" | cut -d '"' -f 4) curl -sL "\$url" -o temp.tar.gz && tar -xzf temp.tar.gz find . -name "\$binary" -type f -exec mv {} "/usr/local/bin/" \; && chmod +x "/usr/local/bin/\$binary" rm -rf temp.tar.gz \$(ls -d */ 2>/dev/null) } # Set architecture-specific patterns if [ "\$ARCH" = "aarch64" ]; then GH_PATTERN="browser_download_url.*linux_arm64.tar.gz" FASTLY_PATTERN="browser_download_url.*linux-arm64.tar.gz" BUTANE_ARCH="aarch64-unknown-linux-gnu" MC_ARCH="linux-arm64" DOCTL_ARCH="linux-arm64" GCP_ARCH="linux-arm" DENO_ARCH="aarch64-unknown-linux-gnu" else GH_PATTERN="browser_download_url.*linux_amd64.tar.gz" FASTLY_PATTERN="browser_download_url.*linux-amd64.tar.gz" BUTANE_ARCH="x86_64-unknown-linux-gnu" MC_ARCH="linux-amd64" DOCTL_ARCH="linux-amd64" GCP_ARCH="linux-x86_64" DENO_ARCH="x86_64-unknown-linux-gnu" fi # GitHub CLI install_github_binary "cli/cli" "gh" "\$GH_PATTERN" # Fastly CLI install_github_binary "fastly/cli" "fastly" "\$FASTLY_PATTERN" # Butane curl -sL "https://github.com/coreos/butane/releases/latest/download/butane-\$BUTANE_ARCH" -o /usr/local/bin/butane # MinIO Client curl -sL "https://dl.min.io/client/mc/release/\$MC_ARCH/mc" -o /usr/local/bin/mc # DigitalOcean CLI curl -sL "https://github.com/digitalocean/doctl/releases/download/v1.118.0/doctl-1.118.0-\$DOCTL_ARCH.tar.gz" | tar -xz && mv doctl /usr/local/bin/ # Google Cloud SDK curl -sL "https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-\$GCP_ARCH.tar.gz" | tar -xz -C /opt/ for tool in gcloud gsutil bq; do ln -sf "/opt/google-cloud-sdk/bin/\$tool" "/usr/local/bin/\$tool"; done chmod +x /usr/local/bin/{butane,mc,doctl} # Deno curl -sL "https://github.com/denoland/deno/releases/download/v2.2.12/deno-\$DENO_ARCH.zip" -o deno.zip unzip -q deno.zip && mv deno /usr/local/bin/ && chmod +x /usr/local/bin/deno && rm -f deno.zip # Linode CLI pip3 install --break-system-packages linode-cli # Azure CLI pip3 install --break-system-packages azure-cli # Clean up build dependencies apk del gcc musl-dev python3-dev libffi-dev openssl-dev cargo make cd / && rm -rf /tmp/* 2>/dev/null && adduser -D app # Create wrapper scripts for binaries for binary in cyclone lang-js; do cat > "/usr/local/bin/\$binary-wrapper" << EOF #!/bin/sh export LD_LIBRARY_PATH="\${GLIBC_PATH}/lib:\${GCC_LIB_PATH}/lib:/usr/lib64:/lib:/usr/lib:\\\$LD_LIBRARY_PATH" exec "/usr/local/bin/\$binary-original" "\\\$@" EOF chmod +x "/usr/local/bin/\$binary-wrapper" done cat > /usr/local/bin/setup-wrappers << 'EOF' #!/bin/sh for bin in cyclone lang-js; do [ -f "/usr/local/bin/\$bin" ] && [ ! -f "/usr/local/bin/\$bin-original" ] && { mv "/usr/local/bin/\$bin" "/usr/local/bin/\$bin-original" ln -sf "/usr/local/bin/\$bin-wrapper" "/usr/local/bin/\$bin" } done EOF chmod +x /usr/local/bin/setup-wrappers && /usr/local/bin/setup-wrappers mknod /dev/null c 1 3 && chmod 666 /dev/null && ssh-keygen -A # Configure boot services for svc in devfs procfs sysfs networking; do rc-update add \$svc boot; done rc-update add local default && rc-update add sshd echo "ttyS0::respawn:/sbin/mingetty --autologin root --noclear ttyS0" >> /etc/inittab sed -i 's/root:*::0:::::/root:::0:::::/g' /etc/shadow echo "LABEL=scripts /mnt/scripts ext4 defaults 0 0" >> /etc/fstab # autostart cyclone cat <<EOF >"/etc/init.d/cyclone" #!/sbin/openrc-run name="cyclone" description="Cyclone" supervisor="supervise-daemon" pidfile="/cyclone/agent.pid" start(){ if [ -f /mnt/scripts/scripts ]; then source /mnt/scripts/scripts fi export HOME=/root cyclone ${cyclone_args[*]} >> /var/log/cyclone.log 2>&1 & } depend(){ need net } EOF chmod +x "/etc/init.d/cyclone" rc-update add cyclone # Set up TAP device route/escape cat <<EOZ >"/etc/network/interfaces" auto lo iface lo inet loopback auto eth0 iface eth0 inet static address 10.0.0.1/30 gateway 10.0.0.2 EOZ EOL # LEAVE CHROOT # For each input, copy the contents into the rootfs # Handle both tar archives (from omnibus packages) and raw binaries for binary_input in "${binary_inputs[@]}"; do if file "$binary_input" | grep -q "tar archive"; then # It's a tar archive, extract it sudo tar -xpf "$binary_input" -C "$ROOTFSMOUNT" else # It's a raw binary, copy it to /usr/local/bin binary_name=$(basename "$binary_input") sudo mkdir -p "$ROOTFSMOUNT/usr/local/bin" sudo cp "$binary_input" "$ROOTFSMOUNT/usr/local/bin/$binary_name" sudo chmod +x "$ROOTFSMOUNT/usr/local/bin/$binary_name" fi done # Must be unmounted then moved with sudo or permission issues will prevent all directories # from copying over for some mysterious reason. sudo umount -fv "$ROOTFSMOUNT" sudo mv -v "$ROOTFS" "$tar_file_out" # Then generate the build metadata # # TODO(johnrwatson): family here needs adjusted to the service/component name as # this doesn't currently support services outside of cyclone. cat <<EOF >"$build_metadata_out" { "family":"cyclone", "variant":"rootfs", "version":"$(jq -r '.canonical_version' <"$git_metadata")", "arch":"$(uname -m | tr '[:upper:]' '[:lower:]')", "os":"$(uname -s | tr '[:upper:]' '[:lower:]')", "commit": "$(jq -r '.commit_hash' <"$git_metadata")", "b3sum": "$(b3sum --no-names "$tar_file_out")" } EOF echo "--- rootfs build complete."

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server