-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
377 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
#!/usr/bin/bash | ||
# | ||
# Connect to a VM created by `vm-run` or `libvirt`. | ||
# | ||
# In case of VMs created by `vm-run`, `<vm_name>` is equivalent to image name | ||
# with a `tmp-` prefix and an optional `-N` suffix when multiple VMs with same name | ||
# are running at the same time, e.g. `tmp-ubuntu2204` or `tmp-ubuntu2204-2`. | ||
# | ||
# Usage: `vm-connect <vm_name>` | ||
# | ||
# | ||
# Copyright 2024 Jakub Kuczys (https://github.com/Jackenmen) | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# https://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
set -eo pipefail | ||
|
||
help() { | ||
echo 'Usage: vm-connect <vm_name>' | ||
} | ||
|
||
vm_name=$1 | ||
if [[ -z "$vm_name" ]]; then | ||
help | ||
exit 2 | ||
fi | ||
|
||
vm_ip=$(virsh domifaddr "$vm_name" | awk -F'[ /]+' '/^[[:blank:]]vnet/ {print $5}' | head -1) | ||
|
||
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "vm@$vm_ip" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
#!/bin/bash | ||
# | ||
# Add a Linux VM image for use by `vm-run`. | ||
# | ||
# Usage: `vm-image-add <image_name> <sums_url> <sum_type> <filename_pattern>` | ||
# | ||
# The script expects to be able to download a Linux image based on | ||
# a provided `CHECKSUMS` file in a format returned by `sha256sum`/`sha512sum` program | ||
# ran with or without `--tag` option. `<filename_pattern>` is used to extract | ||
# the relevant checksum. This setup is generally supported by the data that is provided | ||
# by the distro maintainers though there are 2 notable outliers: | ||
# - Red Hat Enterprise Linux - requires subscription | ||
# - Oracle Linux - does not provide checksum file in a parsable format | ||
# | ||
# You can find examples for different distros at: | ||
# https://github.com/Jackenmen/dotfiles/tree/main/private_dot_config/little-helpers-vm-images | ||
# | ||
# | ||
# Copyright 2024 Jakub Kuczys (https://github.com/Jackenmen) | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# https://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
set -eo pipefail | ||
|
||
help() { | ||
echo 'Usage: vm-image-add <image_name> <sums_url> <sum_type> <filename_pattern>' | ||
} | ||
|
||
image_name=$1 | ||
if [[ -z "$image_name" ]]; then | ||
help | ||
exit 2 | ||
fi | ||
sums_url=$2 | ||
if [[ -z "$sums_url" ]]; then | ||
help | ||
exit 2 | ||
fi | ||
sum_type=$3 | ||
if [[ -z "$sum_type" ]]; then | ||
help | ||
exit 2 | ||
fi | ||
filename_pattern=$4 | ||
if [[ -z "$filename_pattern" ]]; then | ||
help | ||
exit 2 | ||
fi | ||
|
||
image_cfg_dir="${XDG_CONFIG_HOME:-$HOME/.config}/little-helpers-vm-images" | ||
mkdir -p "$image_cfg_dir" | ||
image_file="$image_cfg_dir/$image_name" | ||
if [[ -e "$image_file" ]]; then | ||
echo 'Image with this name already exists:' | ||
echo "- Image name: $(sed '1q;d' "$image_file")" | ||
echo "- Checksums file URL: $(sed '2q;d' "$image_file")" | ||
echo "- Checksum type: $(sed '3q;d' "$image_file")" | ||
echo "- Filename pattern: $(sed '4q;d' "$image_file")" | ||
exit 1 | ||
fi | ||
|
||
echo "$image_name" > "$image_file" | ||
echo "$sums_url" >> "$image_file" | ||
echo "$sum_type" >> "$image_file" | ||
echo "$filename_pattern" >> "$image_file" | ||
|
||
if ! vm-image-fetch "$image_name"; then | ||
exit_code=$? | ||
rm "$image_file" | ||
exit "$exit_code" | ||
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
#!/bin/bash | ||
# | ||
# Fetch latest Linux VM image (added through `vm-image-add`) with the given name. | ||
# | ||
# This tool is typically invoked automatically. | ||
# | ||
# Usage: `vm-image-fetch [-f] <image_name>` | ||
# | ||
# Without the `-f` flag, the image only gets fetched when it isn't already in cache. | ||
# | ||
# | ||
# Copyright 2024 Jakub Kuczys (https://github.com/Jackenmen) | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# https://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
set -eo pipefail | ||
|
||
help() { | ||
echo 'Usage: vm-image-fetch [-f] <image_name>' | ||
} | ||
|
||
force_download=0 | ||
OPTIND=1 | ||
while getopts 'f' flag; do | ||
case "$flag" in | ||
f) | ||
force_download=1 | ||
;; | ||
*) | ||
help | ||
exit 2 | ||
;; | ||
esac | ||
done | ||
shift "$((OPTIND-1))" | ||
|
||
image_name=$1 | ||
if [[ -z "$image_name" ]]; then | ||
help | ||
exit 2 | ||
fi | ||
|
||
image_cfg_dir="${XDG_CONFIG_HOME:-$HOME/.config}/little-helpers-vm-images" | ||
image_file="$image_cfg_dir/$image_name" | ||
if [[ ! -f "$image_file" ]]; then | ||
echo 'ERROR: Image with this name does not exist!' | ||
exit 2 | ||
fi | ||
|
||
sums_url=$(sed '2q;d' "$image_file") | ||
if [[ -z "$sums_url" ]]; then | ||
if [[ ! -f "$image_file" ]]; then | ||
echo 'ERROR: No sum URL set but the image does not already exist.' | ||
exit 1 | ||
fi | ||
echo 'Using cached image as no sums URL is defined.' | ||
exit 0 | ||
fi | ||
|
||
sum_type=$(sed '3q;d' "$image_file") | ||
filename_pattern=$(sed '4q;d' "$image_file") | ||
|
||
checksums=$(curl -L "$sums_url") | ||
checksums+=$(echo '' && echo "$checksums" | sed -nE "s/^${sum_type^^} \(($filename_pattern)\) = ([^ \t]+)\$/\2 \1/p") | ||
matching_files=$(echo "$checksums" | grep -P "^[^ \t]+[ \t]+\*?$filename_pattern\$") | ||
if [[ "$(echo "$matching_files" | wc -l)" -ne 1 ]]; then | ||
echo 'ERROR: Expected only one filename to match but got these matches instead:' | ||
echo "$matching_files" | ||
exit 1 | ||
fi | ||
|
||
base_url="${sums_url%/*}" | ||
new_checksum=$(echo "$matching_files" | awk '{print $1}') | ||
filename=$(echo "$matching_files" | awk '{print $2}') | ||
filename="${filename#\*}" | ||
image_url="$base_url/$filename" | ||
|
||
cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/little-helpers-vm-images" | ||
mkdir -p "$cache_dir" | ||
old_checksum=$( (cat "$cache_dir/$image_name.${sum_type}sum" 2>/dev/null || echo '') | awk '{print $1}' ) | ||
|
||
if [[ "$force_download" -eq 1 || "$old_checksum" != "$new_checksum" ]]; then | ||
echo 'Downloading new version of the image...' | ||
wget -O "$cache_dir/$image_name.img" "$image_url" | ||
echo "$new_checksum $image_name.img" > "$cache_dir/$image_name.${sum_type}sum" | ||
echo 'Image downloaded and cached.' | ||
else | ||
echo 'Cached image already up-to-date.' | ||
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
#!/usr/bin/bash | ||
# | ||
# This scripts run a temporary VM with the given Linux image. | ||
# | ||
# Image's default user is set up with the `~/.ssh/id_rsa` key and additionally, | ||
# `vm` user with `vm` password and the aforementioned key is also created. | ||
# | ||
# See documentation of `vm-image-add` for details about image management. | ||
# | ||
# Usage: `vm-run <image_name>` | ||
# | ||
# The domain (VM) name will be set to `tmp-<image_name>`, optionally suffixed with | ||
# a number (`tmp-<image_name>-2`) when a machine with same name exists. | ||
# | ||
# | ||
# Copyright 2024 Jakub Kuczys (https://github.com/Jackenmen) | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# https://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
set -eo pipefail | ||
|
||
DELETE_DOMAIN=0 | ||
|
||
help() { | ||
echo 'Usage: vm-run <image_name>' | ||
} | ||
|
||
cleanup() { | ||
if [[ "$DELETE_DOMAIN" -eq 1 ]]; then | ||
echo "Undefining VM's domain..." | ||
virsh undefine "$domain_name" | ||
echo 'Shutting down VM...' | ||
if ! virsh shutdown "$domain_name" 2>/dev/null; then | ||
echo 'VM was already powered off.' | ||
fi | ||
fi | ||
echo "Removing VM's temporary directory..." | ||
rm -rf "$vm_dir" | ||
echo 'Removed.' | ||
} | ||
|
||
image_name=$1 | ||
if [[ -z "$image_name" ]]; then | ||
help | ||
exit 2 | ||
fi | ||
|
||
vm-image-fetch "$image_name" | ||
|
||
vm_dir=$(mktemp -d) | ||
trap cleanup EXIT | ||
chgrp libvirt-qemu "$vm_dir" | ||
chmod 0770 "$vm_dir" | ||
base_image_file="${XDG_CACHE_HOME:-$HOME/.cache}/little-helpers-vm-images/$image_name.img" | ||
|
||
image_file="$vm_dir/disk.img" | ||
qemu-img convert -O qcow2 "$base_image_file" "$image_file" | ||
qemu-img resize "$image_file" 50G | ||
chgrp libvirt-qemu "$image_file" | ||
chmod 0660 "$image_file" | ||
|
||
user_data_file="$vm_dir/user-data" | ||
echo '#cloud-config' > "$user_data_file" | ||
echo 'power_state:' >> "$user_data_file" | ||
echo ' mode: poweroff' >> "$user_data_file" | ||
echo ' condition: true' >> "$user_data_file" | ||
echo 'ssh_authorized_keys:' >> "$user_data_file" | ||
echo " - $(<"$HOME/.ssh/id_rsa.pub")" >> "$user_data_file" | ||
echo 'users:' >> "$user_data_file" | ||
echo ' - default' >> "$user_data_file" | ||
echo ' - name: vm' >> "$user_data_file" | ||
echo ' plain_text_passwd: vm' >> "$user_data_file" | ||
echo ' lock_passwd: false' >> "$user_data_file" | ||
echo ' sudo: ALL=(ALL) NOPASSWD:ALL' >> "$user_data_file" | ||
echo ' shell: /bin/bash' >> "$user_data_file" | ||
echo ' ssh_authorized_keys:' >> "$user_data_file" | ||
echo " - $(<"$HOME/.ssh/id_rsa.pub")" >> "$user_data_file" | ||
|
||
domain_name="tmp-$image_name" | ||
i=1 | ||
while virsh dominfo "$domain_name" 2>/dev/null; do | ||
domain_name="tmp-$image_name-$i" | ||
done | ||
|
||
DELETE_DOMAIN=1 | ||
virt-install \ | ||
--memory 2048 --osinfo linux2016 \ | ||
--name "$domain_name" \ | ||
--events on_reboot=restart \ | ||
--cloud-init "user-data=$user_data_file" \ | ||
--disk "$image_file" | ||
|
||
virsh start --console "$domain_name" |