Publish Jellyfin chart and replace direct Kubernetes CD
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
name: Validate, publish and promote Jellyfin chart
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: linux_amd64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Lint and render
|
||||
run: ./scripts/test-chart.sh
|
||||
publish:
|
||||
if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main'
|
||||
needs: validate
|
||||
runs-on: linux_amd64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Derive chart version
|
||||
run: echo "CHART_VERSION=$(./scripts/chart-version.sh)" >> "$GITHUB_ENV"
|
||||
- name: Publish public chart
|
||||
env:
|
||||
PACKAGE_USERNAME: ${{ secrets.PACKAGE_USERNAME }}
|
||||
PACKAGE_TOKEN: ${{ secrets.PACKAGE_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test -n "$PACKAGE_USERNAME" && test -n "$PACKAGE_TOKEN"
|
||||
helm package chart --destination "$RUNNER_TEMP" --version "$CHART_VERSION"
|
||||
curl --fail --silent --show-error --user "$PACKAGE_USERNAME:$PACKAGE_TOKEN" \
|
||||
--request POST --upload-file "$RUNNER_TEMP/jellyfin-$CHART_VERSION.tgz" \
|
||||
https://code.brunner.ninja/api/packages/feedc0de/helm/api/charts
|
||||
- name: Confirm current main
|
||||
env:
|
||||
COMMIT_SHA: ${{ gitea.sha }}
|
||||
run: |
|
||||
git fetch --no-tags origin main
|
||||
test "$(git rev-parse FETCH_HEAD)" = "$COMMIT_SHA"
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: feedc0de/homelab-gitops
|
||||
ref: main
|
||||
token: ${{ secrets.GITOPS_TOKEN }}
|
||||
path: homelab-gitops
|
||||
- name: Promote exact chart
|
||||
working-directory: homelab-gitops
|
||||
run: |
|
||||
set -euo pipefail
|
||||
./scripts/update-chart-version.sh jellyfin "$CHART_VERSION"
|
||||
git config user.name feedc0de
|
||||
git config user.email feedc0de@users.noreply.code.brunner.ninja
|
||||
git add bootstrap/jellyfin.yaml
|
||||
if git diff --cached --quiet; then exit 0; fi
|
||||
git commit -m "Deploy jellyfin chart $CHART_VERSION"
|
||||
for attempt in 1 2 3; do
|
||||
git push origin HEAD:main && exit 0
|
||||
test "$attempt" = 3 && exit 1
|
||||
git fetch origin main
|
||||
git rebase origin/main
|
||||
done
|
||||
@@ -1,72 +0,0 @@
|
||||
name: Validate and deploy Jellyfin
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
KUBECTL_VERSION: v1.36.4
|
||||
KUBERNETES_API: https://host.containers.internal:6443
|
||||
KUBERNETES_TLS_SERVER_NAME: 192.168.0.2
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
name: Validate manifests and scripts
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out source
|
||||
uses: actions/checkout@v4
|
||||
- name: Validate
|
||||
run: ./test.sh
|
||||
|
||||
deploy:
|
||||
name: Deploy to Kubernetes
|
||||
if: gitea.ref == 'refs/heads/main'
|
||||
needs: validate
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out source
|
||||
uses: actions/checkout@v4
|
||||
- name: Install kubectl
|
||||
run: |
|
||||
curl --fail --silent --show-error --location \
|
||||
--output "${RUNNER_TEMP}/kubectl" \
|
||||
"https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl"
|
||||
chmod 0700 "${RUNNER_TEMP}/kubectl"
|
||||
- name: Configure Kubernetes access
|
||||
env:
|
||||
KUBE_CONFIG_BASE64: ${{ secrets.KUBE_CONFIG_BASE64 }}
|
||||
run: |
|
||||
test -n "${KUBE_CONFIG_BASE64}"
|
||||
printf '%s' "${KUBE_CONFIG_BASE64}" | base64 --decode > "${RUNNER_TEMP}/kubeconfig"
|
||||
chmod 0600 "${RUNNER_TEMP}/kubeconfig"
|
||||
export KUBECONFIG="${RUNNER_TEMP}/kubeconfig"
|
||||
"${RUNNER_TEMP}/kubectl" config set-cluster cluster \
|
||||
--server="${KUBERNETES_API}" \
|
||||
--tls-server-name="${KUBERNETES_TLS_SERVER_NAME}"
|
||||
- name: Apply and verify
|
||||
env:
|
||||
COMMIT_SHA: ${{ gitea.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
export KUBECONFIG="${RUNNER_TEMP}/kubeconfig"
|
||||
sed "s|deployment.brunner.ninja/revision: manual|deployment.brunner.ninja/revision: ${COMMIT_SHA}|" \
|
||||
jellyfin.yaml > "${RUNNER_TEMP}/jellyfin.yaml"
|
||||
"${RUNNER_TEMP}/kubectl" apply --dry-run=server --validate=false \
|
||||
--filename "${RUNNER_TEMP}/jellyfin.yaml"
|
||||
"${RUNNER_TEMP}/kubectl" apply --validate=false \
|
||||
--filename "${RUNNER_TEMP}/jellyfin.yaml"
|
||||
for attempt in {1..180}; do
|
||||
IFS='|' read -r generation observed desired updated ready available image <<< "$(
|
||||
"${RUNNER_TEMP}/kubectl" --namespace jellyfin get deployment/jellyfin \
|
||||
--output=jsonpath='{.metadata.generation}|{.status.observedGeneration}|{.spec.replicas}|{.status.updatedReplicas}|{.status.readyReplicas}|{.status.availableReplicas}|{.spec.template.spec.containers[0].image}'
|
||||
)"
|
||||
echo "Rollout ${attempt}/180: generation ${observed}/${generation}, replicas ${updated}/${desired} updated, ${ready}/${desired} ready, ${available}/${desired} available, image ${image}"
|
||||
if [[ "${observed}" == "${generation}" && "${updated}" == "${desired}" \
|
||||
&& "${ready}" == "${desired}" && "${available}" == "${desired}" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
"${RUNNER_TEMP}/kubectl" --namespace jellyfin get deployment/jellyfin --output=yaml
|
||||
exit 1
|
||||
@@ -41,7 +41,8 @@ migration cannot monopolize bcachefs and Ceph. Do not delete the Arch source
|
||||
trees until the Kubernetes server has been verified and a separate backup
|
||||
exists.
|
||||
|
||||
Deploy with `./install.sh`. Jellyfin remains available at
|
||||
The historical migration scripts remain for recovery; routine deployment is
|
||||
owned by Argo CD. Jellyfin remains available at
|
||||
`https://jellyfin.brunner.ninja` and directly on `192.168.0.2:8096`.
|
||||
|
||||
## Monitoring
|
||||
@@ -52,17 +53,24 @@ restricts public access to `/metrics`. Alerts cover scrape availability,
|
||||
restart loops, and PVC capacity. Existing node-exporter, Netdata, SMART, and
|
||||
bcachefs monitoring remain in use.
|
||||
|
||||
## Gitea CI/CD bootstrap
|
||||
## Helm and GitOps
|
||||
|
||||
After initializing this directory as its own repository and creating the Gitea
|
||||
repository, add the remote and perform the first push. Then run:
|
||||
This repository owns the Jellyfin chart: Deployment, Service, media guard,
|
||||
ingress option, metrics middleware, and monitoring. The chart preserves the
|
||||
existing Deployment selector used by `media-storage-offline.sh` and
|
||||
`media-storage-online.sh`. The homelab's node pin, guarded hostPath, existing
|
||||
PVC, public host and resource requests are selected in the
|
||||
[GitOps values](https://code.brunner.ninja/feedc0de/homelab-gitops/src/branch/main/apps/jellyfin).
|
||||
|
||||
```sh
|
||||
./create-ci-kubeconfig.sh
|
||||
helm repo add brunner.ninja https://brunner.ninja/charts
|
||||
helm repo update brunner.ninja
|
||||
helm upgrade --install jellyfin brunner.ninja/jellyfin --namespace jellyfin --create-namespace
|
||||
```
|
||||
|
||||
Store the single output line as `KUBE_CONFIG_BASE64`. The CI identity can only
|
||||
update the already-created Jellyfin resources named in `ci-deployer.yaml`; it
|
||||
cannot read Secrets, run migration Jobs, or alter other workloads. Pull
|
||||
requests validate, while pushes to `main` validate and deploy the pinned
|
||||
upstream image.
|
||||
To install this checkout manually, run `./install.sh -f my-values.yaml` with
|
||||
the target namespace and media mount configured. Main-branch CI validates,
|
||||
publishes the chart using the upstream application Git tag plus `git describe`,
|
||||
then promotes its exact version to GitOps. CI needs `PACKAGE_USERNAME`,
|
||||
`PACKAGE_TOKEN`, and `GITOPS_TOKEN`, but no kubeconfig. The Jellyfin image is
|
||||
the official upstream image and is selected by `Chart.yaml.appVersion`.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: jellyfin
|
||||
description: Jellyfin with guarded host-local media and native metrics
|
||||
type: application
|
||||
version: 0.0.0
|
||||
appVersion: "12.2"
|
||||
@@ -1,8 +1,9 @@
|
||||
{{- if not .Values.persistence.existingClaim }}
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: jellyfin-config
|
||||
namespace: jellyfin
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: jellyfin
|
||||
app.kubernetes.io/component: state
|
||||
@@ -11,26 +12,29 @@ spec:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 80Gi
|
||||
storageClassName: rook-ceph-block-ec
|
||||
storage: {{ .Values.persistence.size }}
|
||||
{{- with .Values.persistence.storageClass }}
|
||||
storageClassName: {{ . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: jellyfin-scripts
|
||||
namespace: jellyfin
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: jellyfin
|
||||
data:
|
||||
prepare.sh: |
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
filesystem_magic=$(stat -f -c '%t' /komposthaufen/multimedia)
|
||||
filesystem_magic=$(stat -f -c '%t' {{ .Values.media.mountPath }})
|
||||
if [ "${filesystem_magic}" != ca451a4e ]; then
|
||||
echo "Expected bcachefs at /komposthaufen/multimedia, found filesystem magic ${filesystem_magic}" >&2
|
||||
echo "Expected bcachefs at {{ .Values.media.mountPath }}, found filesystem magic ${filesystem_magic}" >&2
|
||||
exit 1
|
||||
fi
|
||||
test -d /komposthaufen/multimedia/Videos
|
||||
test -d {{ .Values.media.mountPath }}/Videos
|
||||
system_xml=/state/config/system.xml
|
||||
test -s "${system_xml}"
|
||||
if grep -q '<EnableMetrics>false</EnableMetrics>' "${system_xml}"; then
|
||||
@@ -43,7 +47,7 @@ apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: jellyfin
|
||||
namespace: jellyfin
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: jellyfin
|
||||
app.kubernetes.io/component: server
|
||||
@@ -67,12 +71,9 @@ spec:
|
||||
hostNetwork: true
|
||||
dnsPolicy: ClusterFirstWithHostNet
|
||||
nodeSelector:
|
||||
kubernetes.io/hostname: arschrock
|
||||
media.brunner.ninja/multimedia-ready: "true"
|
||||
{{- toYaml .Values.nodeSelector | nindent 8 }}
|
||||
tolerations:
|
||||
- key: node-role.kubernetes.io/control-plane
|
||||
operator: Exists
|
||||
effect: NoSchedule
|
||||
{{- toYaml .Values.tolerations | nindent 8 }}
|
||||
terminationGracePeriodSeconds: 120
|
||||
securityContext:
|
||||
seccompProfile:
|
||||
@@ -96,7 +97,7 @@ spec:
|
||||
- name: state
|
||||
mountPath: /state
|
||||
- name: media
|
||||
mountPath: /komposthaufen/multimedia
|
||||
mountPath: {{ .Values.media.mountPath }}
|
||||
readOnly: true
|
||||
- name: media-ready
|
||||
mountPath: /media-ready
|
||||
@@ -106,11 +107,11 @@ spec:
|
||||
readOnly: true
|
||||
containers:
|
||||
- name: jellyfin
|
||||
image: docker.io/jellyfin/jellyfin:12.2
|
||||
image: {{ .Values.image.repository }}:{{ default .Chart.AppVersion .Values.image.tag }}
|
||||
imagePullPolicy: IfNotPresent
|
||||
env:
|
||||
- name: TZ
|
||||
value: Europe/Vienna
|
||||
value: {{ .Values.timezone | quote }}
|
||||
- name: HOME
|
||||
value: /var/lib/jellyfin
|
||||
- name: JELLYFIN_DATA_DIR
|
||||
@@ -122,7 +123,7 @@ spec:
|
||||
- name: JELLYFIN_CACHE_DIR
|
||||
value: /var/cache/jellyfin
|
||||
- name: JELLYFIN_PublishedServerUrl
|
||||
value: https://jellyfin.brunner.ninja
|
||||
value: {{ .Values.publishedServerUrl | quote }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8096
|
||||
@@ -152,10 +153,7 @@ spec:
|
||||
periodSeconds: 30
|
||||
timeoutSeconds: 5
|
||||
resources:
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 2Gi
|
||||
ephemeral-storage: 1Gi
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
@@ -178,23 +176,23 @@ spec:
|
||||
- name: cache
|
||||
mountPath: /var/cache/jellyfin
|
||||
- name: media
|
||||
mountPath: /komposthaufen/multimedia
|
||||
mountPath: {{ .Values.media.mountPath }}
|
||||
readOnly: true
|
||||
- name: tmp
|
||||
mountPath: /tmp
|
||||
volumes:
|
||||
- name: state
|
||||
persistentVolumeClaim:
|
||||
claimName: jellyfin-config
|
||||
claimName: {{ default "jellyfin-config" .Values.persistence.existingClaim }}
|
||||
- name: cache
|
||||
emptyDir: {}
|
||||
- name: media
|
||||
hostPath:
|
||||
path: /komposthaufen/multimedia
|
||||
path: {{ .Values.media.hostPath }}
|
||||
type: Directory
|
||||
- name: media-ready
|
||||
hostPath:
|
||||
path: /komposthaufen/multimedia/Videos
|
||||
path: {{ .Values.media.hostPath }}/Videos
|
||||
type: Directory
|
||||
- name: tmp
|
||||
emptyDir: {}
|
||||
@@ -207,7 +205,7 @@ apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: jellyfin
|
||||
namespace: jellyfin
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: jellyfin
|
||||
app.kubernetes.io/component: service
|
||||
@@ -220,32 +218,33 @@ spec:
|
||||
port: 8096
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
{{- if .Values.ingress.enabled }}
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: jellyfin-metrics-internal
|
||||
namespace: jellyfin
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: jellyfin
|
||||
spec:
|
||||
ipAllowList:
|
||||
sourceRange:
|
||||
- 10.0.0.0/8
|
||||
- {{ .Values.monitoring.allowedSourceRange }}
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: jellyfin-brunner-ninja
|
||||
namespace: jellyfin
|
||||
name: {{ default "jellyfin" .Values.ingress.name }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: jellyfin
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
- {{ .Values.ingress.entryPoint }}
|
||||
routes:
|
||||
- kind: Rule
|
||||
match: Host(`jellyfin.brunner.ninja`) && PathPrefix(`/metrics`)
|
||||
match: Host(`{{ required "ingress.host is required" .Values.ingress.host }}`) && PathPrefix(`/metrics`)
|
||||
priority: 100
|
||||
middlewares:
|
||||
- name: jellyfin-metrics-internal
|
||||
@@ -255,19 +254,21 @@ spec:
|
||||
port: http
|
||||
passHostHeader: true
|
||||
- kind: Rule
|
||||
match: Host(`jellyfin.brunner.ninja`)
|
||||
match: Host(`{{ .Values.ingress.host }}`)
|
||||
priority: 10
|
||||
services:
|
||||
- kind: Service
|
||||
name: jellyfin
|
||||
port: http
|
||||
passHostHeader: true
|
||||
{{- end }}
|
||||
{{- if .Values.monitoring.enabled }}
|
||||
---
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
name: jellyfin
|
||||
namespace: jellyfin
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: jellyfin
|
||||
spec:
|
||||
@@ -285,7 +286,7 @@ apiVersion: monitoring.coreos.com/v1
|
||||
kind: PrometheusRule
|
||||
metadata:
|
||||
name: jellyfin
|
||||
namespace: jellyfin
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: jellyfin
|
||||
spec:
|
||||
@@ -294,11 +295,11 @@ spec:
|
||||
rules:
|
||||
- alert: JellyfinMetricsTargetDown
|
||||
expr: |-
|
||||
max(kube_deployment_status_replicas_available{namespace="jellyfin",deployment="jellyfin"}) >= 1
|
||||
max(kube_deployment_status_replicas_available{namespace="{{ .Release.Namespace }}",deployment="jellyfin"}) >= 1
|
||||
and on()
|
||||
(
|
||||
max(up{namespace="jellyfin",service="jellyfin"}) < 1
|
||||
or absent(up{namespace="jellyfin",service="jellyfin"})
|
||||
max(up{namespace="{{ .Release.Namespace }}",service="jellyfin"}) < 1
|
||||
or absent(up{namespace="{{ .Release.Namespace }}",service="jellyfin"})
|
||||
)
|
||||
for: 10m
|
||||
labels:
|
||||
@@ -310,7 +311,7 @@ spec:
|
||||
- alert: JellyfinContainerRestarting
|
||||
expr: |-
|
||||
sum by (container) (
|
||||
increase(kube_pod_container_status_restarts_total{namespace="jellyfin",pod=~"jellyfin-.*"}[15m])
|
||||
increase(kube_pod_container_status_restarts_total{namespace="{{ .Release.Namespace }}",pod=~"jellyfin-.*"}[15m])
|
||||
) > 2
|
||||
for: 5m
|
||||
labels:
|
||||
@@ -318,12 +319,12 @@ spec:
|
||||
component: kubernetes
|
||||
annotations:
|
||||
summary: Jellyfin container is repeatedly restarting
|
||||
description: Container {{ $labels.container }} restarted more than twice in 15 minutes.
|
||||
description: Container {{ "{{ $labels.container }}" }} restarted more than twice in 15 minutes.
|
||||
- alert: JellyfinConfigVolumeFilling
|
||||
expr: |-
|
||||
kubelet_volume_stats_available_bytes{namespace="jellyfin",persistentvolumeclaim="jellyfin-config"}
|
||||
kubelet_volume_stats_available_bytes{namespace="{{ .Release.Namespace }}",persistentvolumeclaim="{{ default "jellyfin-config" .Values.persistence.existingClaim }}"}
|
||||
/
|
||||
kubelet_volume_stats_capacity_bytes{namespace="jellyfin",persistentvolumeclaim="jellyfin-config"} < 0.15
|
||||
kubelet_volume_stats_capacity_bytes{namespace="{{ .Release.Namespace }}",persistentvolumeclaim="{{ default "jellyfin-config" .Values.persistence.existingClaim }}"} < 0.15
|
||||
for: 30m
|
||||
labels:
|
||||
severity: warning
|
||||
@@ -331,3 +332,4 @@ spec:
|
||||
annotations:
|
||||
summary: Jellyfin configuration volume is filling
|
||||
description: Less than 15 percent of the Jellyfin configuration PVC remains available.
|
||||
{{- end }}
|
||||
@@ -0,0 +1,23 @@
|
||||
image:
|
||||
repository: docker.io/jellyfin/jellyfin
|
||||
tag: ""
|
||||
media:
|
||||
hostPath: /media
|
||||
mountPath: /media
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
persistence:
|
||||
existingClaim: ""
|
||||
storageClass: ""
|
||||
size: 80Gi
|
||||
timezone: UTC
|
||||
publishedServerUrl: ""
|
||||
resources: {}
|
||||
ingress:
|
||||
enabled: false
|
||||
name: ""
|
||||
host: ""
|
||||
entryPoint: websecure
|
||||
monitoring:
|
||||
enabled: false
|
||||
allowedSourceRange: 10.0.0.0/8
|
||||
+8
-29
@@ -1,31 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
project_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
expected_context=kubernetes-admin@kubernetes
|
||||
context=$(kubectl config current-context)
|
||||
|
||||
if [ "${context}" != "${expected_context}" ]; then
|
||||
echo "Expected Kubernetes context ${expected_context}, found ${context}." >&2
|
||||
exit 1
|
||||
fi
|
||||
kubectl apply --filename "${project_dir}/namespace.yaml"
|
||||
if kubectl --namespace jellyfin get job/jellyfin-archlinux-migration >/dev/null 2>&1; then
|
||||
kubectl --namespace jellyfin wait --for=condition=complete \
|
||||
job/jellyfin-archlinux-migration --timeout=5s
|
||||
fi
|
||||
kubectl apply --dry-run=server --filename "${project_dir}/jellyfin.yaml"
|
||||
kubectl apply --filename "${project_dir}/jellyfin.yaml"
|
||||
kubectl --namespace jellyfin scale deployment/jellyfin --replicas=1
|
||||
kubectl --namespace jellyfin rollout status deployment/jellyfin --timeout=30m
|
||||
|
||||
pod=$(kubectl --namespace jellyfin get pod \
|
||||
--selector=app.kubernetes.io/name=jellyfin,app.kubernetes.io/component=server \
|
||||
--output=jsonpath='{.items[0].metadata.name}')
|
||||
node=$(kubectl --namespace jellyfin get pod "${pod}" --output=jsonpath='{.spec.nodeName}')
|
||||
image=$(kubectl --namespace jellyfin get deployment jellyfin --output=jsonpath='{.spec.template.spec.containers[0].image}')
|
||||
test "${node}" = arschrock
|
||||
test "${image}" = docker.io/jellyfin/jellyfin:12.2
|
||||
kubectl --namespace jellyfin get pod "${pod}" --output=wide
|
||||
printf 'Jellyfin is ready on %s with image %s.\n' "${node}" "${image}"
|
||||
release=${RELEASE_NAME:-jellyfin}
|
||||
namespace=${NAMESPACE:-jellyfin}
|
||||
version=$(./scripts/chart-version.sh)
|
||||
package_dir=$(mktemp -d)
|
||||
trap 'rm -rf -- "$package_dir"' EXIT
|
||||
helm package ./chart --version "$version" --destination "$package_dir" >/dev/null
|
||||
helm upgrade --install "$release" "$package_dir/jellyfin-$version.tgz" \
|
||||
--namespace "$namespace" --create-namespace --wait --timeout 30m "$@"
|
||||
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
description=$(git describe --tags --long --match 'v[0-9]*' --dirty)
|
||||
case "$description" in *-dirty) echo 'Refusing dirty tree' >&2; exit 1 ;; esac
|
||||
version=${description#v}
|
||||
case "$version" in *-0-g*) version=${version%%-0-g*} ;; esac
|
||||
version=$(printf '%s\n' "$version" | sed -E 's/^([0-9]+\.[0-9]+)(-[0-9]+-g[0-9a-f]+)?$/\1.0\2/')
|
||||
printf '%s\n' "$version"
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
helm lint chart
|
||||
rendered=$(helm template jellyfin chart --namespace jellyfin --set persistence.existingClaim=retained-data --set media.hostPath=/example/media --set media.mountPath=/media --set ingress.enabled=true --set ingress.host=jellyfin.example.org --set monitoring.enabled=true)
|
||||
printf '%s\n' "$rendered" | grep -q 'claimName: retained-data'
|
||||
printf '%s\n' "$rendered" | grep -q 'type: Recreate'
|
||||
printf '%s\n' "$rendered" | grep -q 'path: /example/media'
|
||||
printf '%s\n' "$rendered" | grep -q 'ca451a4e'
|
||||
if printf '%s\n' "$rendered" | grep -q '^kind: PersistentVolumeClaim$'; then exit 1; fi
|
||||
helm template jellyfin chart --namespace jellyfin | grep -q '^kind: PersistentVolumeClaim$'
|
||||
sh -n install.sh scripts/chart-version.sh scripts/test-chart.sh
|
||||
Reference in New Issue
Block a user