Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions labs/lab1.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,20 @@ git config --global commit.gpgsign true
git config --global tag.gpgsign true
```

Tell the platform your SSH key is a **signing key**:
- GitHub: Settings → SSH and GPG keys → **New SSH key**, key type **Signing Key**
- GitLab: Profile → SSH Keys → tick "Usage type: Authentication & signing"
Now register the key on the platform. GitHub treats **Authentication** and **Signing** as *separate* roles for the same key, so you add it under both:

- **Authentication Key** — lets you `clone` / `fetch` / `push` over SSH (`git@github.com:…`). If you cloned over HTTPS, or have never seen `ssh -T git@github.com` greet you by name, you don't have one configured yet — add it now or the `upstream` SSH remote will fail in Lab 2.
- **Signing Key** — gives your commits the **Verified** badge.

- 🐙 GitHub: Settings → SSH and GPG keys → **New SSH key** → add the **same** `~/.ssh/id_ed25519.pub` **twice**, once with Key type **Authentication Key** and once with **Signing Key**.
- 🦊 GitLab: Profile → SSH Keys → a single key with **Usage type: Authentication & signing** covers both.

Confirm authentication works before moving on:

```bash
ssh -T git@github.com
# expect: Hi YOUR_USERNAME! You've successfully authenticated...
```

### 1.4: Make a Signed Commit

Expand Down Expand Up @@ -303,7 +314,8 @@ In `submissions/lab1.md`:
## Common Pitfalls

- 🪤 **PR template doesn't auto-populate** — make sure the template is on `main` *before* opening the PR
- 🪤 **Commits show "Unverified"** — the SSH key must be added as a *Signing Key* on GitHub (not just an authentication key)
- 🪤 **Commits show "Unverified"** — the key must also be added as a **Signing Key** on GitHub; an Authentication Key alone won't verify commits (they're separate roles — see §1.3)
- 🪤 **`git@github.com: Permission denied (publickey)` on clone/fetch/push** — the *reverse* gap: your key is registered for signing but not as an **Authentication Key**. Add it as Authentication too (§1.3) and confirm with `ssh -T git@github.com`. Quick unblock for the *public* upstream: `git remote set-url upstream https://github.com/inno-devops-labs/DevOps-Intro.git`
- 🪤 **`git push` rejected on `main`** — that's the bonus rule working as designed; push to `feature/lab1` instead
- 🪤 **`gpg.format=ssh` ignored** — confirm Git ≥ 2.34: `git --version`
- 🪤 **Pushed to the wrong branch** — `git switch feature/lab1` before `git push`
Expand Down
1 change: 1 addition & 0 deletions labs/lab2.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ git bisect reset

## Common Pitfalls

- 🪤 **`git@github.com: Permission denied (publickey)` on `git fetch upstream`** — *not* a remote-config bug (the error is at the SSH layer, before Git reads the repo). Your key isn't registered for **authentication** on GitHub — and a **Signing Key** (Lab 1) does *not* count for auth, they're separate roles. Add the same `~/.ssh/id_ed25519.pub` as an **Authentication Key** (Lab 1 §1.3), verify with `ssh -T git@github.com`, then re-run. To unblock right now, the public upstream fetches over HTTPS with no key: `git remote set-url upstream https://github.com/inno-devops-labs/DevOps-Intro.git`
- 🪤 **`reset --hard` without committing first** — your *uncommitted* edits really *are* gone (reflog only saves committed work). Always check `git status` first
- 🪤 **`tag -v` says "no signature"** — you used `git tag NAME` instead of `git tag -a -s NAME -m "..."`
- 🪤 **Rebase conflicts** — resolve, then `git rebase --continue`. Never `git rebase --skip` unless you know what you're skipping
Expand Down
21 changes: 21 additions & 0 deletions labs/lab3.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,23 @@ Tips:
- GitLab: `parallel:matrix:`
- Set `fail-fast: false` (GH) or equivalent so a single bad cell doesn't cancel the others — you want to *see* which combo broke

> ⚠️ **The matrix renames your checks — update branch protection (1.6) or your PR blocks forever.** A matrixed `test` job reports as `test (1.23)` and `test (1.24)`; the old required check named `test` will sit at *"Expected — Waiting for status to be reported"* indefinitely, even though every real check is green. Two fixes:
>
> 1. **Quick:** in the branch-protection rule, replace `vet`/`test` with the matrixed names (`vet (1.23)`, `vet (1.24)`, `test (1.23)`, `test (1.24)`).
> 2. **Robust (recommended):** add one aggregation job and require *only* it — then the matrix can change freely without touching protection settings:
>
> ```yaml
> ci-ok:
> if: always()
> needs: [vet, test, lint]
> runs-on: ubuntu-24.04
> steps:
> - run: |
> test "${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}" = "false"
> ```
>
> The `if: always()` matters — without it, a failed `needs` job *skips* `ci-ok`, and a skipped required check lets the PR through on some configurations.

### 2.3: Skip docs-only changes

Edit your trigger so the pipeline runs **only** when something in `app/` or your CI config itself changes. README edits should not burn 4 minutes of CI time.
Expand All @@ -179,6 +196,8 @@ Capture wall-clock times from the CI UI for three scenarios:

> 💡 To get a clean baseline, temporarily disable each optimization with a commit, take a screenshot of the run time, then restore.

> 🧪 **Expect the cache rows to be boring — that's the finding, not a failure.** QuickNotes has **zero third-party dependencies** (look at `app/go.mod` — no `require` block, no `go.sum`), so the module cache has nothing to store and total wall-clock barely moves with `cache: true` vs `cache: false`. Most of your 60–80 s is runner provisioning, checkout, and the Go toolchain download — none of which `setup-go`'s cache touches. Report what you measured and *explain why* (that's design question **f** in disguise). To see where caching *would* pay, compare the **per-step** durations (`setup-go`, `go test`) instead of job totals, and note which step a real dependency-heavy project would save on.

### 2.5: Document

In `submissions/lab3.md`:
Expand Down Expand Up @@ -284,6 +303,8 @@ Answer in 4-6 sentences:
- 🪤 **Forgot `working-directory` (or `cd app`) for Go commands** — Go modules live in `app/`, not the repo root; commands run from the root will fail with "no Go files"
- 🪤 **`fail-fast: true` (the GH Actions default) in a matrix** — one fail cancels the others; you can't see *which* combo broke
- 🪤 **Branch protection set on someone else's fork's `main`** — you can only protect *your* fork's `main`. The upstream course repo has its own protection
- 🪤 **PR stuck on "Expected — Waiting for status to be reported" after adding the matrix** — the matrix renamed `test` → `test (1.23)`/`test (1.24)`, but branch protection still requires the old `test` context, which will never report again. Update the required-check names or switch to the `ci-ok` aggregation job (see §2.2)
- 🪤 **"Caching didn't speed anything up"** — on a zero-dependency module that's the *correct* result, not a mistake (see §2.4); don't pad the timing table with numbers you didn't observe
- 🪤 **`golangci-lint` version not pinned** — "latest" pulls a new release tomorrow that may flag your code with new rules. Pin `v2.5.0` exactly
- 🪤 **GitLab CI: incorrect anchor syntax** (`<<: *name`) — GitLab is strict; use the in-platform CI Lint tool (`Project → CI/CD → Editor → Validate`)
- 🪤 **Cache hits expire after 7 days of inactivity on GH** — that's expected; the cache key is what protects you against poisoning
Expand Down
23 changes: 23 additions & 0 deletions submissions/lab4-assets/curl-post.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 127.0.0.1:8080...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /notes HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.82.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 39
>
} [39 bytes data]
* Mark bundle as not supporting multiuse
< HTTP/1.1 201 Created
< Content-Type: application/json
< Date: Tue, 16 Jun 2026 16:46:12 GMT
< Content-Length: 92
<
{ [92 bytes data]
100 131 100 92 100 39 15219 6451 --:--:-- --:--:-- --:--:-- 26200
* Connection #0 to host localhost left intact
{"id":6,"title":"trace me","body":"in flight","created_at":"2026-06-16T16:46:12.62574443Z"}
Expand Down
3 changes: 3 additions & 0 deletions submissions/lab4-assets/dig-example.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
$ dig +short example.com @1.1.1.1
104.20.23.154
172.66.147.243
1 change: 1 addition & 0 deletions submissions/lab4-assets/go-run-processes.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root 4460 298 12 19:48 pts/0 00:00:00 /snap/go/11200/bin/go run .
3 changes: 3 additions & 0 deletions submissions/lab4-assets/ip-route.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
$ ip route show
default via 172.19.128.1 dev eth0 proto kernel
172.19.128.0/20 dev eth0 proto kernel scope link src 172.19.130.184
2 changes: 2 additions & 0 deletions submissions/lab4-assets/journalctl-quicknotes.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
$ journalctl --user -u quicknotes -n 20 || true
-- No entries --
Binary file added submissions/lab4-assets/lab4-trace.pcap
Binary file not shown.
43 changes: 43 additions & 0 deletions submissions/lab4-assets/lab4-trace.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
19:46:12.625388 IP 127.0.0.1.39140 > 127.0.0.1.8080: Flags [S], seq 4277809752, win 65495, options [mss 65495,sackOK,TS val 2900709604 ecr 0,nop,wscale 7], length 0
E..<.}@.@..<..............2X.........0.........
..P.........
19:46:12.625400 IP 127.0.0.1.8080 > 127.0.0.1.39140: Flags [S.], seq 2244450212, ack 4277809753, win 65483, options [mss 65495,sackOK,TS val 2900709604 ecr 2900709604,nop,wscale 7], length 0
E..<..@.@.<...................2Y.....0.........
..P...P.....
19:46:12.625409 IP 127.0.0.1.39140 > 127.0.0.1.8080: Flags [.], ack 1, win 512, options [nop,nop,TS val 2900709604 ecr 2900709604], length 0
E..4.~@.@..C..............2Y.........(.....
..P...P.
19:46:12.625606 IP 127.0.0.1.39140 > 127.0.0.1.8080: Flags [P.], seq 1:176, ack 1, win 512, options [nop,nop,TS val 2900709604 ecr 2900709604], length 175: HTTP: POST /notes HTTP/1.1
E.....@.@.................2Y...............
..P...P.POST /notes HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.82.0
Accept: */*
Content-Type: application/json
Content-Length: 39

{"title":"trace me","body":"in flight"}
19:46:12.625632 IP 127.0.0.1.8080 > 127.0.0.1.39140: Flags [.], ack 176, win 511, options [nop,nop,TS val 2900709604 ecr 2900709604], length 0
E..4..@.@.....................3......(.....
..P...P.
19:46:12.631249 IP 127.0.0.1.8080 > 127.0.0.1.39140: Flags [P.], seq 1:206, ack 176, win 512, options [nop,nop,TS val 2900709610 ecr 2900709604], length 205: HTTP: HTTP/1.1 201 Created
E.....@.@..K..................3............
..P...P.HTTP/1.1 201 Created
Content-Type: application/json
Date: Tue, 16 Jun 2026 16:46:12 GMT
Content-Length: 92

{"id":6,"title":"trace me","body":"in flight","created_at":"2026-06-16T16:46:12.62574443Z"}

19:46:12.631275 IP 127.0.0.1.39140 > 127.0.0.1.8080: Flags [.], ack 206, win 511, options [nop,nop,TS val 2900709610 ecr 2900709610], length 0
E..4..@.@..A..............3....r.....(.....
..P...P.
19:46:12.631448 IP 127.0.0.1.39140 > 127.0.0.1.8080: Flags [F.], seq 176, ack 206, win 512, options [nop,nop,TS val 2900709610 ecr 2900709610], length 0
E..4..@.@..@..............3....r.....(.....
..P...P.
19:46:12.631508 IP 127.0.0.1.8080 > 127.0.0.1.39140: Flags [F.], seq 206, ack 177, win 512, options [nop,nop,TS val 2900709610 ecr 2900709610], length 0
E..4..@.@..................r..3 .....(.....
..P...P.
19:46:12.631522 IP 127.0.0.1.39140 > 127.0.0.1.8080: Flags [.], ack 207, win 512, options [nop,nop,TS val 2900709610 ecr 2900709610], length 0
E..4..@.@..?..............3 ...s.....(.....
..P...P.
4 changes: 4 additions & 0 deletions submissions/lab4-assets/mtr-localhost.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
$ mtr -rwc 5 localhost
Start: 2026-06-16T19:47:44+0300
HOST: DESKTOP-I0DHHPT Loss% Snt Last Avg Best Wrst StDev
1.|-- localhost 0.0% 5 0.0 0.0 0.0 0.0 0.0
2 changes: 2 additions & 0 deletions submissions/lab4-assets/outsidein-1-process.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
$ ps -ef | grep quicknotes
root 4535 4460 0 19:48 pts/0 00:00:00 /root/.cache/go-build/01/01b36d0cffeef160fa8af2f501e7375bd20c7b28a5467c110d6e43fef651f8f2-d/quicknotes
2 changes: 2 additions & 0 deletions submissions/lab4-assets/outsidein-2-listening.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
$ ss -tlnp | grep 8080
LISTEN 0 4096 *:8080 *:* users:(("quicknotes",pid=4535,fd=3))
2 changes: 2 additions & 0 deletions submissions/lab4-assets/outsidein-3-health.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
$ curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/health
200
1 change: 1 addition & 0 deletions submissions/lab4-assets/outsidein-4-firewall.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
$ sudo iptables -L -n -v 2>/dev/null || sudo nft list ruleset 2>/dev/null || true
2 changes: 2 additions & 0 deletions submissions/lab4-assets/outsidein-5-dns.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
$ dig +short localhost
127.0.0.1
3 changes: 3 additions & 0 deletions submissions/lab4-assets/qn-broken.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
2026/06/16 19:48:28 quicknotes listening on :8080 (notes loaded: 6)
2026/06/16 19:48:28 listen: listen tcp :8080: bind: address already in use
exit status 1
1 change: 1 addition & 0 deletions submissions/lab4-assets/repair-health.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"notes":6,"status":"ok"}
1 change: 1 addition & 0 deletions submissions/lab4-assets/repair-listening.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
LISTEN 0 4096 *:8080 *:* users:(("quicknotes",pid=4886,fd=3))
2 changes: 2 additions & 0 deletions submissions/lab4-assets/repair-server.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
2026/06/16 20:02:09 quicknotes listening on :8080 (notes loaded: 6)
2026/06/16 20:02:33 shutting down
2 changes: 2 additions & 0 deletions submissions/lab4-assets/ss-8080.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
$ ss -tlnp | grep :8080
LISTEN 0 4096 *:8080 *:* users:(("quicknotes",pid=4337,fd=3))
Loading