From 32611bfd1666a79e40cd6c51a51585d276458027 Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Sun, 23 Aug 2026 00:09:40 +1000 Subject: [PATCH] Deploy mediamover v0.1.0 into arrstack (#393) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deploys the released mediamover v0.1.0 into the arrstack namespace: a single-replica server (API + UI on :8080) that browses the mediafs source, queues per-file copy/move operations into the media PVCs, and spawns one worker Job per file using its own image — its ServiceAccount/RBAC grants the Job and Pod access that requires. All PVs/PVCs it mounts are already live. - Adds `apps/base/arrstack/mediamover/` with SA, Role/RoleBinding (batch jobs create/get/list/watch/delete; pods get/list/watch; pods/log get/list), Deployment, Service, Gateway, HTTPRoute - Runs `mediamover server` with `--src-root /srv/src --src-pvc mediafs`, `--dst-roots movies=/srv/dst/movies,tv=/srv/dst/tv`, `--dst-pvc movies=media-movies,tv=media-tv`, `--namespace arrstack`, `--image ...mediamover:v0.1.0` - Mounts mediafs RW at /srv/src (move deletes the source) plus media-movies and media-tv destinations; keeps 1 replica with Recreate strategy since the queue is in-memory - Names the Service `mediamover` on 8080 to match the worker callback default `http://mediamover.arrstack.svc:8080` - Exposes the UI at https://mediamover.k8s.syd1.au.unkin.net via a dedicated internal Gateway (pdbmux pattern); the existing arrproxy Gateway is external and hostname-locked to arrstack.unkin.net so it cannot carry this route - Probes hit `GET /api/limit` (the server has no dedicated health endpoint); registers `mediamover` in the arrstack base kustomization Reviewed-on: https://git.unkin.net/unkin/argocd-apps/pulls/393 Co-authored-by: unkin-agent Co-committed-by: unkin-agent --- apps/base/arrstack/kustomization.yaml | 1 + apps/base/arrstack/mediamover/deployment.yaml | 95 +++++++++++++++++++ apps/base/arrstack/mediamover/gateway.yaml | 40 ++++++++ apps/base/arrstack/mediamover/httproute.yaml | 49 ++++++++++ .../arrstack/mediamover/kustomization.yaml | 11 +++ apps/base/arrstack/mediamover/rbac.yaml | 48 ++++++++++ apps/base/arrstack/mediamover/service.yaml | 19 ++++ .../arrstack/mediamover/serviceaccount.yaml | 6 ++ 8 files changed, 269 insertions(+) create mode 100644 apps/base/arrstack/mediamover/deployment.yaml create mode 100644 apps/base/arrstack/mediamover/gateway.yaml create mode 100644 apps/base/arrstack/mediamover/httproute.yaml create mode 100644 apps/base/arrstack/mediamover/kustomization.yaml create mode 100644 apps/base/arrstack/mediamover/rbac.yaml create mode 100644 apps/base/arrstack/mediamover/service.yaml create mode 100644 apps/base/arrstack/mediamover/serviceaccount.yaml diff --git a/apps/base/arrstack/kustomization.yaml b/apps/base/arrstack/kustomization.yaml index a3846b7..762a342 100644 --- a/apps/base/arrstack/kustomization.yaml +++ b/apps/base/arrstack/kustomization.yaml @@ -19,3 +19,4 @@ resources: - prowlarr - nzbget - arrproxy + - mediamover diff --git a/apps/base/arrstack/mediamover/deployment.yaml b/apps/base/arrstack/mediamover/deployment.yaml new file mode 100644 index 0000000..ff75984 --- /dev/null +++ b/apps/base/arrstack/mediamover/deployment.yaml @@ -0,0 +1,95 @@ +--- +# mediamover server: REST API + UI on :8080. Spawns one worker Job per queued +# file using this same image with `worker` args (Job spec lives in code; workers +# run as the default ServiceAccount and mount the same PVCs). The queue is +# in-memory, so keep a single replica; a restart just loses queued entries. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mediamover + namespace: arrstack +spec: + replicas: 1 + selector: + matchLabels: + app: mediamover + strategy: + type: Recreate + template: + metadata: + labels: + app: mediamover + spec: + serviceAccountName: mediamover + automountServiceAccountToken: true + securityContext: + runAsNonRoot: true + runAsUser: 65532 + runAsGroup: 65532 + fsGroup: 65532 + seccompProfile: + type: RuntimeDefault + containers: + - name: server + image: artifactapi.k8s.syd1.au.unkin.net/docker-internal/mediamover:v0.1.0 + imagePullPolicy: IfNotPresent + args: + - server + - --src-root=/srv/src + - --src-pvc=mediafs + - --dst-roots=movies=/srv/dst/movies,tv=/srv/dst/tv + - --dst-pvc=movies=media-movies,tv=media-tv + - --namespace=arrstack + - --image=artifactapi.k8s.syd1.au.unkin.net/docker-internal/mediamover:v0.1.0 + ports: + - containerPort: 8080 + name: http + protocol: TCP + volumeMounts: + # RW: move mode deletes the source file after a successful copy. + - name: src + mountPath: /srv/src + - name: dst-movies + mountPath: /srv/dst/movies + - name: dst-tv + mountPath: /srv/dst/tv + livenessProbe: + httpGet: + path: /api/limit + port: http + initialDelaySeconds: 10 + periodSeconds: 30 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /api/limit + port: http + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi + volumes: + - name: src + persistentVolumeClaim: + claimName: mediafs + - name: dst-movies + persistentVolumeClaim: + claimName: media-movies + - name: dst-tv + persistentVolumeClaim: + claimName: media-tv + restartPolicy: Always diff --git a/apps/base/arrstack/mediamover/gateway.yaml b/apps/base/arrstack/mediamover/gateway.yaml new file mode 100644 index 0000000..678f53b --- /dev/null +++ b/apps/base/arrstack/mediamover/gateway.yaml @@ -0,0 +1,40 @@ +--- +# Internal front for mediamover. The existing arrstack (arrproxy) Gateway is +# external and hostname-locked to arrstack.unkin.net, so this tool gets its own +# internal Gateway following the cluster convention (cf. pdbmux). +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + labels: + traefik.io/instance: internal + annotations: + cert-manager.io/cluster-issuer: vault-issuer + cert-manager.io/common-name: mediamover.k8s.syd1.au.unkin.net + cert-manager.io/private-key-size: "4096" + external-dns.alpha.kubernetes.io/hostname: mediamover.k8s.syd1.au.unkin.net + external-dns.alpha.kubernetes.io/target: 198.18.200.4 + name: mediamover + namespace: arrstack +spec: + gatewayClassName: traefik-internal + listeners: + - allowedRoutes: + namespaces: + from: Same + hostname: mediamover.k8s.syd1.au.unkin.net + name: http + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: Same + hostname: mediamover.k8s.syd1.au.unkin.net + name: https + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: "" + kind: Secret + name: mediamover-tls + mode: Terminate diff --git a/apps/base/arrstack/mediamover/httproute.yaml b/apps/base/arrstack/mediamover/httproute.yaml new file mode 100644 index 0000000..63e7da0 --- /dev/null +++ b/apps/base/arrstack/mediamover/httproute.yaml @@ -0,0 +1,49 @@ +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: mediamover-http-redirect + namespace: arrstack +spec: + hostnames: + - mediamover.k8s.syd1.au.unkin.net + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: mediamover + sectionName: http + rules: + - filters: + - type: RequestRedirect + requestRedirect: + scheme: https + statusCode: 301 + matches: + - path: + type: PathPrefix + value: / +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: mediamover + namespace: arrstack +spec: + hostnames: + - mediamover.k8s.syd1.au.unkin.net + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: mediamover + sectionName: https + rules: + - backendRefs: + - group: "" + kind: Service + name: mediamover + port: 8080 + weight: 1 + matches: + - path: + type: PathPrefix + value: / diff --git a/apps/base/arrstack/mediamover/kustomization.yaml b/apps/base/arrstack/mediamover/kustomization.yaml new file mode 100644 index 0000000..0032d8d --- /dev/null +++ b/apps/base/arrstack/mediamover/kustomization.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - serviceaccount.yaml + - rbac.yaml + - deployment.yaml + - service.yaml + - gateway.yaml + - httproute.yaml diff --git a/apps/base/arrstack/mediamover/rbac.yaml b/apps/base/arrstack/mediamover/rbac.yaml new file mode 100644 index 0000000..5ea25f3 --- /dev/null +++ b/apps/base/arrstack/mediamover/rbac.yaml @@ -0,0 +1,48 @@ +--- +# The server creates one worker Job per queued file and polls Job/Pod state to +# track progress and clean up. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: mediamover + namespace: arrstack +rules: + - apiGroups: + - batch + resources: + - jobs + verbs: + - create + - get + - list + - watch + - delete + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - pods/log + verbs: + - get + - list +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: mediamover + namespace: arrstack +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: mediamover +subjects: + - kind: ServiceAccount + name: mediamover + namespace: arrstack diff --git a/apps/base/arrstack/mediamover/service.yaml b/apps/base/arrstack/mediamover/service.yaml new file mode 100644 index 0000000..a46be90 --- /dev/null +++ b/apps/base/arrstack/mediamover/service.yaml @@ -0,0 +1,19 @@ +--- +# Must stay named "mediamover" on port 8080: worker Jobs default their progress +# callback URL to http://mediamover..svc:8080. +apiVersion: v1 +kind: Service +metadata: + name: mediamover + namespace: arrstack +spec: + internalTrafficPolicy: Cluster + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: http + selector: + app: mediamover + sessionAffinity: None + type: ClusterIP diff --git a/apps/base/arrstack/mediamover/serviceaccount.yaml b/apps/base/arrstack/mediamover/serviceaccount.yaml new file mode 100644 index 0000000..0f5cd6a --- /dev/null +++ b/apps/base/arrstack/mediamover/serviceaccount.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: mediamover + namespace: arrstack