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
14 changes: 14 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,20 @@ run-homelab-restore:
argo submit --from workflowtemplate/homelab-restore-drill \
-n {{ argo_ns }} --wait --log

# ── Service-catalog media lane (#80) ─────────────────────────────────────────

# Run the media-service lane through the service-catalog pipeline.
# Requires the service-catalog-pipeline WorkflowTemplate from #79.
# Usage: just run-service-media
# Usage: just run-service-media image-tag=lts
# Usage: just run-service-media branch=feat/my-branch
run-service-media image-tag="latest" branch="main":
argo submit --from workflowtemplate/service-catalog-pipeline \
-p lane=media \
-p image-tag={{ image-tag }} \
-p branch={{ branch }} \
-n {{ argo_ns }} --wait --log

# ── Ghost maintenance ─────────────────────────────────────────────────────────

# Patch ghost OTel collector config to remove noisy process scraper (#117)
Expand Down
20 changes: 20 additions & 0 deletions RUNBOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,26 @@ Golden disks can be patched by workflow after key rotation; titan disk key refre
| VM stuck `Terminating` | KubeVirt controller race with launcher cleanup | Delete the `virt-launcher-*` pod and let reconciliation finish |
| `run-gnome-tests` pod fails at startup | Workflow template structure error, often misplaced `volumes:` | Fix the template in git and let ArgoCD reconcile it |
| WorkflowTemplate change appears ignored | Workflow was submitted before the new template was reconciled | Verify ArgoCD revision, wait or sync, then submit a new workflow |
| Media lane image pull timeout | Jellyfin image is large (~500MB+); cold pull exceeds readiness probe | Increase `initialDelaySeconds` in the readiness probe or pre-pull the image |
| Media lane PVC stuck Pending | No default StorageClass or insufficient capacity | Verify `kubectl get sc` shows a default class; check PV availability |
| Media lane persistence test fails | Sentinel file lost across restart | Check PVC reclaim policy is `Retain`; verify volume is not ephemeral |

## Media-service lane

The media lane (`lane=media`) is the first concrete workload in the
service-catalog pipeline. It deploys a Jellyfin (linuxserver.io) container
with two PVCs (config 1Gi, data 10Gi) and validates the base behaviors
from the workload contract (#66):

- Raw-manifest deployment in Kubernetes
- Persistent state/config survives restart
- Service health endpoint reachable on port 8096
- Clean teardown via namespace deletion
- Operator-readable evidence in workflow logs

GPU transcoding and device passthrough are explicitly deferred to #63.

Entry point: `just run-service-media`

## Historical notes

Expand Down
33 changes: 33 additions & 0 deletions WORKFLOWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,39 @@ selinux=0, sudoers) on an existing golden disk without rebuilding it.
|---|---|---|
| `image-tag` | `latest` | Disk dir under `/var/tmp/bluefin-golden/`. |

### `service-catalog-pipeline` (lane=media)

First concrete lane in the service-catalog pipeline. Deploys a
Jellyfin-based (linuxserver.io) media workload into an ephemeral
namespace and validates deployment, persistence, reachability,
environment injection, and teardown.

Requires the `service-catalog-pipeline` WorkflowTemplate from #79.

| Parameter | Default | Notes |
|---|---|---|
| `lane` | `media` | Fixed for this lane |
| `image-tag` | `latest` | Passed through to the pipeline |
| `branch` | `main` | testing-lab branch to clone |

Wall-clock: ~3–5 min (depends on image pull).

```
just run-service-media # default
just run-service-media image-tag=lts # LTS variant
just run-service-media branch=feat/my-branch # test a branch
```

Test coverage:
- Deployment readiness, pod Running, service endpoints, lane labels
- Config PVC (1Gi) and data PVC (10Gi) bound and writable
- Cluster DNS resolution and HTTP reachability on port 8096
- PUID/PGID/TZ environment variable injection
- Config and data sentinel files survive rollout restart
- No cluster-scoped resource leaks
- Mount/storage observability artifacts for operator evidence
- GPU transcoding and device passthrough explicitly skipped (→ #63)

---

## Supporting templates (called via `templateRef`)
Expand Down
19 changes: 19 additions & 0 deletions argo/service-catalog-media.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: svc-media-
namespace: argo
labels:
bluefin.io/lane: svc-media
spec:
serviceAccountName: argo
workflowTemplateRef:
name: service-catalog-pipeline
arguments:
parameters:
- name: lane
value: "media"
- name: image-tag
value: "latest"
- name: branch
value: "main"
1 change: 1 addition & 0 deletions tests/service_catalog/media/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Media-service lane for the service-catalog pipeline."""
Empty file.
106 changes: 106 additions & 0 deletions tests/service_catalog/media/fixtures/manifests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: media-config
labels:
app.kubernetes.io/name: media-service
app.kubernetes.io/component: config
bluefin.io/lane: svc-media
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: media-data
labels:
app.kubernetes.io/name: media-service
app.kubernetes.io/component: data
bluefin.io/lane: svc-media
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: media-service
labels:
app.kubernetes.io/name: media-service
bluefin.io/lane: svc-media
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: media-service
template:
metadata:
labels:
app.kubernetes.io/name: media-service
bluefin.io/lane: svc-media
spec:
containers:
- name: jellyfin
image: lscr.io/linuxserver/jellyfin:latest
ports:
- name: http
containerPort: 8096
protocol: TCP
env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: TZ
value: "Etc/UTC"
volumeMounts:
- name: config
mountPath: /config
- name: data
mountPath: /data/media
readinessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: "2"
memory: 4Gi
volumes:
- name: config
persistentVolumeClaim:
claimName: media-config
- name: data
persistentVolumeClaim:
claimName: media-data
---
apiVersion: v1
kind: Service
metadata:
name: media-service
labels:
app.kubernetes.io/name: media-service
bluefin.io/lane: svc-media
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: media-service
ports:
- name: http
port: 8096
targetPort: http
protocol: TCP
106 changes: 106 additions & 0 deletions tests/service_catalog/media/manifests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: media-config
labels:
app.kubernetes.io/name: media-service
app.kubernetes.io/component: config
bluefin.io/lane: svc-media
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: media-data
labels:
app.kubernetes.io/name: media-service
app.kubernetes.io/component: data
bluefin.io/lane: svc-media
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: media-service
labels:
app.kubernetes.io/name: media-service
bluefin.io/lane: svc-media
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: media-service
template:
metadata:
labels:
app.kubernetes.io/name: media-service
bluefin.io/lane: svc-media
spec:
containers:
- name: jellyfin
image: lscr.io/linuxserver/jellyfin:latest
ports:
- name: http
containerPort: 8096
protocol: TCP
env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: TZ
value: "Etc/UTC"
volumeMounts:
- name: config
mountPath: /config
- name: data
mountPath: /data/media
readinessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: "2"
memory: 4Gi
volumes:
- name: config
persistentVolumeClaim:
claimName: media-config
- name: data
persistentVolumeClaim:
claimName: media-data
---
apiVersion: v1
kind: Service
metadata:
name: media-service
labels:
app.kubernetes.io/name: media-service
bluefin.io/lane: svc-media
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: media-service
ports:
- name: http
port: 8096
targetPort: http
protocol: TCP
Loading