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
13 changes: 13 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## Goal
<!-- What does this PR accomplish? 1 sentence. -->

## Changes
-

## Testing
<!-- How did you verify it? -->

## Checklist
- [ ] Title is a clear sentence (≤ 70 chars)
- [ ] Commits are signed (`git log --show-signature`)
- [ ] `submissions/labN.md` updated
96 changes: 96 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: DevOps Intro Workflow

on:
push:
branches: [ main ]
paths:
- 'app/**'
- '.github/workflows/**'
pull_request:
branches: [ main ]
paths:
- 'app/**'
- '.github/workflows/**'

permissions:
contents: read

env:
GOFLAGS: -buildvcs=false

jobs:
vet:
runs-on: ubuntu-24.04
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 1

- name: Setup Go compiler
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
with:
go-version: '1.24'
cache: true
cache-dependency-path: app/go.sum

- name: Run go vet
run: go vet ./...
working-directory: app

test:
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
go: ['1.23', '1.24']
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 1

- name: Setup Go compiler
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
with:
go-version: ${{ matrix.go }}
cache: true
cache-dependency-path: app/go.sum

- name: Run unit tests
run: go test -race -count=1 ./...
working-directory: app

lint:
runs-on: ubuntu-24.04
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 1

- name: Setup Go compiler
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
with:
go-version: '1.24'
cache: true
cache-dependency-path: app/go.sum

- name: Run golangci-lint
uses: golangci/golangci-lint-action@1481404843c368bc19ca9406f87d6e0fc97bdcfd # v7.0.0
with:
version: v2.5.0
working-directory: app

ci-ok:
if: always()
needs: [vet, test, lint]
runs-on: ubuntu-24.04
steps:
- name: Check status of dependent jobs
run: |
if ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}; then
echo "One or more dependent jobs failed or were cancelled."
exit 1
fi
echo "All dependent jobs completed successfully."
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,5 @@ Thumbs.db
# *.sbom.cdx.json, zap-*.html/json, trivy-*.txt (Lab 9 scan evidence)
# flake.nix, flake.lock (Lab 11)
# wasm/main.go, spin.toml, go.sum (Lab 12)
data/
app/data/
24 changes: 24 additions & 0 deletions app/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# syntax=docker/dockerfile:1

FROM golang:1.24.6-bookworm AS builder
WORKDIR /src
COPY go.mod go.su[m] ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build \
-trimpath \
-ldflags='-s -w' \
-o /out/quicknotes .
RUN mkdir -p /data-empty

FROM busybox:1.37-uclibc AS busybox

FROM gcr.io/distroless/static:nonroot
WORKDIR /app
COPY --from=builder /out/quicknotes /app/quicknotes
COPY --from=builder /src/seed.json /app/seed.json
COPY --from=busybox /bin/wget /bin/wget
COPY --from=builder --chown=65532:65532 /data-empty /data
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/app/quicknotes"]
3 changes: 3 additions & 0 deletions app/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ func (sw *statusWriter) WriteHeader(code int) {
func (s *Server) wrap(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
sw := &statusWriter{ResponseWriter: w, code: 200}
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("Cross-Origin-Resource-Policy", "same-origin")
w.Header().Set("Cache-Control", "no-store")
h(sw, r)
s.requestsTotal.Add(1)
if c, ok := s.requestsByCode[sw.code]; ok {
Expand Down
1 change: 0 additions & 1 deletion app/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,3 @@ func TestMetrics_ExposesPrometheusFormat(t *testing.T) {
}
}
}

29 changes: 29 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
services:
quicknotes:
build: ./app
image: quicknotes:lab6
ports:
- "8080:8080"
environment:
ADDR: ":8080"
DATA_PATH: /data/notes.json
SEED_PATH: /app/seed.json
healthcheck:
test: ["CMD", "/bin/wget", "-q", "-O", "-", "http://127.0.0.1:8080/health"]
interval: 10s
timeout: 3s
retries: 3
start_period: 5s
cap_drop:
- ALL
read_only: true
tmpfs:
- /tmp
security_opt:
- no-new-privileges:true
volumes:
- quicknotes-data:/data
restart: unless-stopped

volumes:
quicknotes-data:
25 changes: 25 additions & 0 deletions evidence/lab12/00-windows-attempt.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
=== Windows attempt (abandoned) ===
Host: Windows 11, Go 1.26.5, Spin 4.0.2, TinyGo 0.41.1

1. Spin 4 template no longer uses TinyGo at all.
Generated spin.toml build command: 'go tool componentize-go build'
Generated go.mod requires: github.com/spinframework/spin-go-sdk/v3 v3.0.0
Tool: github.com/bytecodealliance/componentize-go v0.3.3
The lab's requirements for -buildmode=c-shared and TinyGo describe a toolchain
Spin 3.x used; Spin 4 replaced it.

2. componentize-go's own installer requests a file that does not exist:
GET .../v0.3.3/componentize-go-windows-amd64.tar.gz -> 404
The release publishes componentize-go-windows-amd64.zip instead.

3. Installing the binary manually got further, but the scaffold ships no wit/
directory, so the build fails with 'failed to read path for WIT [wit]'.

4. Supplying a wit/ directory from componentize-go's own wasip2 example got
further still, and then the Go linker panicked:
runtime.wasiOnIdle.wrapinfo: missing section for relocation target
panic: runtime error: invalid memory address or nil pointer dereference
cmd/link/internal/ld.(*pclntab).generateFuncdata
This is a linker bug in the host Go toolchain, not a configuration error.

Conclusion: the lab was completed on macOS instead.
53 changes: 53 additions & 0 deletions evidence/lab12/01-task1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
=== toolchain ===
spin 4.0.2 (bfc7543 2026-06-23)
go version go1.25.12 darwin/arm64 (build used this; the 1.26.5 shown by a shell without GOTOOLCHAIN=local crashes the linker)
GOTOOLCHAIN=

=== spin.toml ===
#:schema https://schemas.spinframework.dev/spin/manifest-v2/latest.json

spin_manifest_version = 2

[application]
name = "moscow-time"
version = "0.1.0"
authors = ["Elvira <239804565+HNS2112@users.noreply.github.com>"]
description = ""

[[trigger.http]]
route = "/time"
component = "moscow-time"

[component.moscow-time]
source = "main.wasm"
allowed_outbound_hosts = []
[component.moscow-time.build]
command = "go tool componentize-go build"
watch = ["**/*.go", "go.mod"]

=== go.mod ===
module github.com/moscow_time

go 1.25.5

require github.com/spinframework/spin-go-sdk/v3 v3.0.0

require (
github.com/apparentlymart/go-userdirs v0.0.0-20200915174352-b0c018a67c13 // indirect
github.com/bytecodealliance/componentize-go v0.3.3 // indirect
github.com/gofrs/flock v0.13.0 // indirect
go.bytecodealliance.org/pkg v0.2.1 // indirect
golang.org/x/sys v0.37.0 // indirect
)

tool github.com/bytecodealliance/componentize-go
=== artifact ===
-rw-r--r-- 1 elvira staff 5242031 Aug 10 17:56 wasm/moscow-time/main.wasm

=== GET /time ===
{
"unix": 1786374003,
"iso": "2026-08-10T18:00:03+03:00",
"hour_minute": "18:00",
"zone": "Europe/Moscow (UTC+3)"
}
4 changes: 4 additions & 0 deletions evidence/lab12/02-warm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `curl -s http://127.0.0.1:3000/time` | 10.5 ± 0.8 | 8.8 | 11.8 | 1.00 |
| `curl -s http://localhost:8080/health` | 11.3 ± 1.1 | 7.8 | 12.8 | 1.07 ± 0.13 |
6 changes: 6 additions & 0 deletions evidence/lab12/03-cold-docker.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
=== Docker cold start (5 samples) ===
docker cold-1 = 137.5 ms
docker cold-2 = 143.7 ms
docker cold-3 = 139.9 ms
docker cold-4 = 131.5 ms
docker cold-5 = 140.7 ms
6 changes: 6 additions & 0 deletions evidence/lab12/04-cold-spin.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
=== Spin cold start (5 samples) ===
spin cold-1 = 171.7 ms
spin cold-2 = 136.3 ms
spin cold-3 = 137.2 ms
spin cold-4 = 134.6 ms
spin cold-5 = 139.9 ms
3 changes: 3 additions & 0 deletions evidence/lab12/05-wasmtime.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `wasmtime run --env REQUEST_METHOD=GET --env PATH_INFO=/time main.wasm` | 20.2 ± 1.2 | 17.7 | 23.5 | 1.00 |
18 changes: 18 additions & 0 deletions evidence/lab12/06-bonus.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
=== wasm-cli artifact ===
-rwxr-xr-x 1 elvira staff 3184098 Aug 10 18:06 main.wasm

=== build command ===
GOOS=wasip1 GOARCH=wasm go build -o main.wasm .

=== run command + output ===
wasmtime run --env REQUEST_METHOD=GET --env PATH_INFO=/time main.wasm
Content-Type: application/json

{"unix":1786374493,"iso":"2026-08-10T18:08:13+03:00","hour_minute":"18:08","zone":"Europe/Moscow (UTC+3)"}

=== Spin component under bare wasmtime run ===
Error: failed to run main module `../wasm/moscow-time/main.wasm`

Caused by:
0: component imports instance `wasi:http/types@0.3.0-rc-2026-03-15`, but a matching implementation was not found in the linker
1: instance export `fields` has the wrong type
Loading