diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..619f3cb40 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,17 @@ +# Pull Request Template + +## Goal + + +## Changes + +- + +## Testing + + +## Checklist + +- [ ] Title is a clear sentence (≤ 70 chars) +- [ ] Commits are signed (`git log --show-signature`) +- [ ] `submissions/labN.md` updated diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 000000000..8a9bd44f4 --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,42 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +GO_VERSION = "1.24.5".freeze + +Vagrant.configure("2") do |config| + config.vm.box = "bento/ubuntu-24.04" + config.vm.hostname = "quicknotes-vm" + config.vm.network "forwarded_port", guest: 8080, host: 18080, host_ip: "127.0.0.1" + config.vm.synced_folder "./app", "/home/vagrant/app" + + config.vm.provider "virtualbox" do |vb| + vb.name = "quicknotes-vm" + vb.cpus = 2 + vb.memory = 1024 + end + + config.vm.provision "shell", env: { "GO_VERSION" => GO_VERSION }, inline: <<-'SHELL' + set -euo pipefail + + if command -v go >/dev/null 2>&1 && go version | grep -q "go${GO_VERSION} "; then + echo "Go ${GO_VERSION} already installed; skipping." + exit 0 + fi + + echo "Installing Go ${GO_VERSION}..." + tarball="go${GO_VERSION}.linux-amd64.tar.gz" + curl -fsSLO "https://go.dev/dl/${tarball}" + rm -rf /usr/local/go + tar -C /usr/local -xzf "${tarball}" + rm -f "${tarball}" + + echo 'export PATH=$PATH:/usr/local/go/bin' > /etc/profile.d/go.sh + chmod 644 /etc/profile.d/go.sh + + # symlink so non-interactive `vagrant ssh -c 'go ...'` (no /etc/profile.d) finds go + ln -sf /usr/local/go/bin/go /usr/local/bin/go + ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt + + /usr/local/go/bin/go version + SHELL +end diff --git a/submissions/lab5.md b/submissions/lab5.md new file mode 100644 index 000000000..c5cabe82a --- /dev/null +++ b/submissions/lab5.md @@ -0,0 +1,171 @@ +# Lab 5 Submission + +## Task 1: Vagrant Up + Run QuickNotes Inside + +### Vagrantfile + +The `Vagrantfile` at the repo root: + +- uses the `bento/ubuntu-24.04` box (Ubuntu 24.04 LTS) +- sets the hostname to `quicknotes-vm` +- forwards `127.0.0.1:18080` -> guest `:8080` +- syncs host `./app` -> guest `/home/vagrant/app` +- caps the VM at 2 vCPU / 1024 MB RAM +- installs Go 1.24.5 via an idempotent shell provisioner + +See [Vagrantfile](../Vagrantfile). + +### `vagrant up` — first 10 lines + +```text +Bringing machine 'default' up with 'virtualbox' provider... +==> default: Box 'bento/ubuntu-24.04' could not be found. Attempting to find and install... + default: Box Provider: virtualbox + default: Box Version: >= 0 +==> default: Loading metadata for box 'bento/ubuntu-24.04' + default: URL: https://vagrantcloud.com/api/v2/vagrant/bento/ubuntu-24.04 +==> default: Adding box 'bento/ubuntu-24.04' (v202510.26.0) for provider: virtualbox (amd64) + default: Downloading: https://vagrantcloud.com/bento/boxes/ubuntu-24.04/versions/202510.26.0/providers/virtualbox/amd64/vagrant.box +==> default: Successfully added box 'bento/ubuntu-24.04' (v202510.26.0) for 'virtualbox (amd64)'! +==> default: Importing base box 'bento/ubuntu-24.04'... +``` + +Tail of provisioning confirms the Go install: + +```text +==> default: Mounting shared folders... + default: D:/Desktop/DevOps-Intro/app => /home/vagrant/app +==> default: Running provisioner: shell... + default: Installing Go 1.24.5... + default: go version go1.24.5 linux/amd64 +``` + +> **Environment notes:** +> +> - Vagrant 2.4.9 crashes with `Log level must be in 0..8` when fetching a box. Fix: set `VAGRANT_CLOUD_LOG=error` before `vagrant up`. +> - Host had Hyper-V active (WSL2 + Docker). VirtualBox 7.2 ran the VM anyway, so no need to disable it. + +### Verify + +**Inside the VM:** + +```bash +$ vagrant ssh -c 'go version' +go version go1.24.5 linux/amd64 + +$ vagrant ssh -c 'cd /home/vagrant/app && go build -o /tmp/qn . \ + && (DATA_PATH=/tmp/notes.json setsid /tmp/qn >/tmp/qn.log 2>&1 break -> verify -> restore -> verify + +```bash +$ vagrant snapshot save quicknotes-clean-go124 +==> default: Snapshotting the machine as 'quicknotes-clean-go124'... +==> default: Snapshot saved! ... + +$ vagrant ssh -c 'sudo rm -rf /usr/local/go /usr/local/bin/go \ + /usr/local/bin/gofmt /etc/profile.d/go.sh' + +$ vagrant ssh -c 'go version' +bash: line 1: /usr/local/bin/go: No such file or directory (exit 127) + +$ time vagrant snapshot restore quicknotes-clean-go124 + +$ vagrant ssh -c 'go version' +go version go1.24.5 linux/amd64 +$ curl -s http://localhost:18080/health +{"notes":4,"status":"ok"} +``` + +The snapshot captured the **live** machine (RAM included), so the restore +brought the QuickNotes process back with the same PID `3099`, still listening +on `:8080`. + +### 6. Restore timing + +```text +real 0m21.592s +user 0m0.015s +sys 0m0.000s +``` + +### 2.2 Design questions + +**e) Snapshots are not backups — why?** + +A snapshot lives on the **same disk and host** as the VM, so it's useless when +that disk dies, the laptop is lost/stolen, or run `vagrant destroy` — it +goes with the original. It also can't undo corruption that predates it: if data was already bad (or encrypted by ransomware) when snapshotted, restoring just brings the bad state back. A backup is a separate, off-host, retained copy + +**f) Copy-on-write — 10 snapshots vs 1?** + +Copy-on-write means a snapshot doesn't copy the disk. The current disk is +frozen as a read-only base and new writes go to a delta file, so each snapshot +costs only the blocks that changed since the last one. Ten snapshots ≈ base + +ten small deltas, usually far less than 10× the disk. The cost is on reads, +which must walk the whole delta chain + +**g) When is snapshotting an antipattern?** + +When the chain gets **long**. Each snapshot adds a delta layer, so long chains +mean slower reads, unbounded disk growth, and fragility — losing one delta +breaks everything after it. Snapshots should be short-lived (take → test → +restore-or-delete), not long-term history or a backup substitute. Hoarding VM +state as a "pet" instead of rebuilding from code ("cattle") is the antipattern