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