A simple REST API for managing the "floor" (who's allowed to talk) in a push-to-talk radio group. Only one user can hold the floor per group at a time. If a holder doesn't release it manually, it auto-releases after a timeout.
-
openapi.yaml— the API spec (contract). Describes the two endpoints, their inputs/outputs, and status codes. Not read by the code at runtime — it's the blueprint the server is built to match. -
server0.js— the actual server, written in plain Node.js (no frameworks/libraries). Implements:POST /groups/{groupId}/floor— obtain the floor. Returns200on success,400ifuserIdis missing/invalid,409if someone else already holds it.DELETE /groups/{groupId}/floor/{userId}— release the floor. Returns200on success,403if that user doesn't currently hold it.- State is kept in-memory (a
MapofgroupId -> { userId, timer }), reset whenever the server restarts. - Floor timeout: each obtain starts a 30s timer (
FLOOR_TIMEOUT_MS) that auto-releases the floor if nobody releases it manually. Releasing manually cancels the timer.
-
test.sh— automated test script. Starts the server, sends a sequence of requests withcurl, and checks the returned HTTP status codes match what's expected (200 → 409 → 403 → 200). Exits non-zero if any check fails — this is what CI runs. -
Dockerfile— recipe to package the app into a container image: starts from a Node.js base image, copiesserver0.jsin, and runsnode server0.json container start. -
.github/workflows/ci.yml— GitHub Actions workflow. On every push or pull request, spins up a clean Ubuntu VM, checks out the code, installs Node, and runstest.sh. Fails the build if tests fail. -
k8s-deployment.yaml— Kubernetes manifest with two objects:- a Deployment running one replica of the
floor-control-apiimage on port8080(imagePullPolicy: Never, since the image is loaded locally rather than pulled from a registry) - a Service (
NodePort) that exposes it inside the cluster on port8080
- a Deployment running one replica of the
node server0.js
Server listens on http://localhost:8080.
chmod +x test.sh
./test.sh
docker build -t floor-control-api .
docker run -p 8080:8080 floor-control-api
Requires Docker and kind,
and kubectl.
# 1. Create a local cluster (one-time, unless already created)
kind create cluster --name floor-control
# 2. Build the image
docker build -t floor-control-api:latest .
# 3. Load the image into the kind cluster
# (kind runs its own isolated container runtime, so it can't see
# images that only exist in your regular Docker until you load them)
kind load docker-image floor-control-api:latest --name floor-control
# 4. Deploy
kubectl apply -f k8s-deployment.yaml
# 5. Check the pod is running
kubectl get pods
# 6. Forward a local port into the cluster so you can reach it
kubectl port-forward service/floor-control-api 8080:8080With port-forward running, http://localhost:8080 behaves the same as
running the container directly with Docker.
After changing code, redeploy with:
docker build -t floor-control-api:latest .
kind load docker-image floor-control-api:latest --name floor-control
kubectl rollout restart deployment floor-control-apiUseful commands:
kubectl get pods # pod status / restart count
kubectl logs -l app=floor-control-api # container logs
kubectl logs -l app=floor-control-api -f # follow logs live
kubectl describe pod -l app=floor-control-api # crash reasons, eventsTear down:
kubectl delete -f k8s-deployment.yaml
kind delete cluster --name floor-control# Obtain the floor
curl -X POST http://localhost:8080/groups/group-alpha-123/floor \
-H "Content-Type: application/json" -d '{"userId":"user-456"}'
# Release the floor
curl -X DELETE http://localhost:8080/groups/group-alpha-123/floor/user-456