Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
2 changes: 2 additions & 0 deletions httpserver-python-restartpolicie/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FROM scratch
COPY server.py /app/server.py
Comment thread
grobeert marked this conversation as resolved.
Outdated
5 changes: 5 additions & 0 deletions httpserver-python-restartpolicie/Kraftfile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
spec: v0.6
name: restart-demo
runtime: python:3.12
rootfs: ./Dockerfile
cmd: ["/usr/bin/python3", "/app/server.py"]
Comment thread
grobeert marked this conversation as resolved.
Outdated
209 changes: 209 additions & 0 deletions httpserver-python-restartpolicie/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
# Restart Policy Demo — Unikraft Cloud

A simple Python HTTP server used to demonstrate and test the three restart policies available on Unikraft Cloud: `never`, `always`, and `on-failure`.

---

## Prerequisites

- [`kraft`](https://unikraft.org/docs/cli) / [`unikraft`](https://unikraft.com/docs/cli) CLI installed
- A [Unikraft Cloud](https://unikraft.cloud) account with an API token
- `curl` and `jq` installed

---

## Setup

```bash
# Set your API token
export UKC_TOKEN=<your-token>

# Set your metro
export UKC_METRO=fra

# Login to Unikraft Cloud
unikraft login
```

---

## Server Endpoints

| Endpoint | Description |
|----------|-------------|
| `GET /` | Returns `Hello from restart-demo!` |
| `GET /exit?code=N` | Calls `sys.exit(N)` — simulates crash or clean exit |
Comment thread
grobeert marked this conversation as resolved.
Comment on lines +32 to +35

---

## Testing with kraft / unikraft CLI

Both CLIs work the same way, just swap `kraft cloud` with `unikraft`.

### Deploy the 3 instances

```bash
# kraft CLI
kraft cloud deploy -M 256M -p 443:8080/http+tls --name restart-never --restart never .
kraft cloud deploy -M 256M -p 443:8080/http+tls --name restart-always --restart always .
kraft cloud deploy -M 256M -p 443:8080/http+tls --name restart-on-failure --restart on-failure .

# unikraft CLI — build first, then run
unikraft build . --output restart-never:latest
unikraft build . --output restart-always:latest
unikraft build . --output restart-on-failure:latest

unikraft run --metro fra -m 256M -p 443:8080/http+tls --restart never --name restart-never --image restart-never:latest
unikraft run --metro fra -m 256M -p 443:8080/http+tls --restart always --name restart-always --image restart-always:latest
unikraft run --metro fra -m 256M -p 443:8080/http+tls --restart on-failure --name restart-on-failure --image restart-on-failure:latest
Comment thread
grobeert marked this conversation as resolved.
```

### Check instances

```bash
# kraft CLI
kraft cloud instance list

# unikraft CLI
unikraft instances list
```

### Trigger scenarios

```bash
# hello world — confirm server is up
curl https://<fqdn>/

# simulate crash (exit code 1)
curl https://<fqdn>/exit?code=1

# simulate clean exit (exit code 0)
curl https://<fqdn>/exit?code=0
```

### Check Restart Behavior & Table

```bash
# wait 2-3 seconds then check state
kraft cloud instance list
# or
unikraft instances list
```

---

## Testing via API (bash scripts)

### Create an instance

```bash
chmod +x api/create.sh
./api/create.sh <name> <image> <policy> [memory_mb]

# Examples
./api/create.sh restart-never restart-demo:latest never
./api/create.sh restart-always restart-demo:latest always
./api/create.sh restart-on-failure restart-demo:latest on-failure
```

### List all instances

```bash
./api/list.sh

# compact view
./api/list.sh | jq '.data.instances[] | {name, state, restart_policy, fqdn: .service_group.domains[0].fqdn}'
```

### Interact with the server

```bash
chmod +x api/interact.sh

# hello world
./api/interact.sh <fqdn> hello

# simulate crash
./api/interact.sh <fqdn> exit 1

# simulate clean exit
./api/interact.sh <fqdn> exit 0
```

### Delete an instance

```bash
chmod +x api/delete.sh
./api/delete.sh <uuid>
```

---

## Expected Behavior

| Policy | exit code 0 | exit code != 0 | Use case |
|--------|-------------|----------------|----------|
| `never` | ❌ does not restart | ❌ does not restart | One-shot jobs, batch tasks |
| `always` | ✅ restarts | ✅ restarts | Critical services, always-on |
| `on-failure` | ❌ does not restart | ✅ restarts | Production servers, resilient workloads |

---

## Test Scenarios Summary

### Scenario 1 — `never`

```bash
# deploy
kraft cloud deploy -M 256M -p 443:8080/http+tls --name restart-never --restart never .

# trigger crash
curl https://<fqdn>/exit?code=1

# check — instance should be stopped and stay stopped
kraft cloud instance list
```

### Scenario 2 — `on-failure`

```bash
# deploy
kraft cloud deploy -M 256M -p 443:8080/http+tls --name restart-on-failure --restart on-failure .

# trigger crash — should restart
curl https://<fqdn>/exit?code=1
sleep 3 && kraft cloud instance list # STATE: running ✅

# clean exit — should NOT restart
curl https://<fqdn>/exit?code=0
sleep 3 && kraft cloud instance list # STATE: stopped ✅
```

### Scenario 3 — `always`

```bash
# deploy
kraft cloud deploy -M 256M -p 443:8080/http+tls --name restart-always --restart always .

# clean exit — should restart anyway
curl https://<fqdn>/exit?code=0
sleep 3 && kraft cloud instance list # STATE: running ✅

# crash — should also restart
curl https://<fqdn>/exit?code=1
sleep 3 && kraft cloud instance list # STATE: running ✅
```

---

## Cleanup

```bash
# kraft CLI
kraft cloud instance remove restart-never
kraft cloud instance remove restart-always
kraft cloud instance remove restart-on-failure

# or via API
./api/delete.sh <uuid>
```
24 changes: 24 additions & 0 deletions httpserver-python-restartpolicie/api/create.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash

# Folosire: ./create.sh <nume> <imagine> <policy> [memory_mb]
# Exemplu: ./create.sh restart-never grobeert/restart-demo:latest never 256

NAME=$1
IMAGE=$2
POLICY=$3
MEMORY=${4:-256}

curl -s \
-X POST \
-H "Authorization: Bearer $UKC_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"$NAME\",
\"image\": \"$IMAGE\",
\"memory_mb\": $MEMORY,
\"restart_policy\": \"$POLICY\",
\"autostart\": true
}" \
https://api.fra.unikraft.cloud/v1/instances \
| jq .
Comment thread
grobeert marked this conversation as resolved.
Outdated

12 changes: 12 additions & 0 deletions httpserver-python-restartpolicie/api/delete.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

# Folosire: ./delete.sh <uuid>
# Exemplu: ./delete.sh 1fa6c541-10e5-4ca8-8828-7597c6facc58

UUID=$1

curl -s \
-X DELETE \
-H "Authorization: Bearer $UKC_TOKEN" \
https://api.fra.unikraft.cloud/v1/instances/$UUID \
| jq .
Comment thread
grobeert marked this conversation as resolved.
17 changes: 17 additions & 0 deletions httpserver-python-restartpolicie/api/interact.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash

# Folosire: ./interact.sh <fqdn> <comanda> [exit_code]
# Exemple:
# ./interact.sh purple-pine-j7i7rl30.fra.unikraft.app hello
# ./interact.sh purple-pine-j7i7rl30.fra.unikraft.app exit 1
# ./interact.sh purple-pine-j7i7rl30.fra.unikraft.app exit 0

FQDN=$1
CMD=$2
CODE=${3:-1}

if [ "$CMD" = "hello" ]; then
curl -s https://$FQDN/
elif [ "$CMD" = "exit" ]; then
curl -s https://$FQDN/exit?code=$CODE
fi
Comment thread
grobeert marked this conversation as resolved.
Outdated
8 changes: 8 additions & 0 deletions httpserver-python-restartpolicie/api/list.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash

# Folosire: ./list.sh

curl -s \
-H "Authorization: Bearer $UKC_TOKEN" \
https://api.fra.unikraft.cloud/v1/instances \
| jq .
Comment thread
grobeert marked this conversation as resolved.
41 changes: 41 additions & 0 deletions httpserver-python-restartpolicie/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import sys
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import urlparse, parse_qs
Comment thread
grobeert marked this conversation as resolved.
Outdated

PORT = 8080

class Handler(BaseHTTPRequestHandler):
def do_GET(self):
parsed = urlparse(self.path)
params = parse_qs(parsed.query)

if parsed.path == "/":
self._respond(200, "Hello from restart-demo!\n")

elif parsed.path == "/health":
self._respond(200, "OK\n")

elif parsed.path == "/exit":
try:
code = int(params.get("code", ["0"])[0])
except ValueError:
self._respond(400, "Invalid exit code\n")
return
Comment thread
grobeert marked this conversation as resolved.
Outdated
self._respond(200, f"Exiting with code {code}...\n")
self.wfile.flush()
sys.exit(code)
else:
self._respond(404, "Not found\n")

def _respond(self, status, body):
self.send_response(status)
self.send_header("Content-Type", "text/plain")
self.end_headers()
self.wfile.write(body.encode())

def log_message(self, format, *args):
print(f"[LOG] {self.address_string()} - {format % args}", flush=True)

if __name__ == "__main__":
print(f"Server open on port {PORT}", flush=True)
HTTPServer(("0.0.0.0", PORT), Handler).serve_forever()
Expand Down