diff --git a/.github/workflows/lint-test.yaml b/.github/workflows/lint-test.yaml index 1a747828..2f671eb5 100644 --- a/.github/workflows/lint-test.yaml +++ b/.github/workflows/lint-test.yaml @@ -5,10 +5,122 @@ on: workflow_call: workflow_dispatch: pull_request: + branches: + - master paths: - - 'charts/**' + - 'charts/plex-media-server/**' jobs: + docs: + name: Bump Chart Version and Re-Generate Docs + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: gabe565/setup-helm-docs-action@v1 + + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.ref }} + + - name: Set up chart-testing + uses: helm/chart-testing-action@v2 + + - name: Check if version bump is needed + id: check_bump + run: | + set +e + ct lint --config .github/linters/ct.yaml --check-version-increment --target-branch ${{ github.event.pull_request.base.ref }} | grep -q 'chart version not ok. Needs a version bump!' + needsBump=$? + + if [ $needsBump -eq 0 ]; then + echo "needsBump=true" >> "$GITHUB_OUTPUT" + else + echo "needsBump=false" >> "$GITHUB_OUTPUT" + fi + + - name: Cache binaries + id: cache-bin + if: ${{ steps.check_bump.outputs.needsBump == 'true' }} + uses: actions/cache@v4 + env: + cache-name: cache-semver + with: + path: bin + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('bin/semver') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - name: Setup semver + if: ${{ steps.check_bump.outputs.needsBump == 'true' && steps.cache-bin.outputs.cache-hit != 'true' }} + run: | + mkdir -p bin + wget -O bin/semver \ + https://raw.githubusercontent.com/fsaintjacques/semver-tool/3.4.0/src/semver + chmod +x bin/semver + + - name: Setup PATH + if: ${{ steps.check_bump.outputs.needsBump == 'true' }} + run: | + echo "$GITHUB_WORKSPACE/bin" >> "$GITHUB_PATH" + + - name: Compute version + if: ${{ steps.check_bump.outputs.needsBump == 'true' }} + id: get_version + env: + PATCH_LABEL: "${{ contains(github.event.pull_request.labels.*.name, 'chart: patch') }}" + MINOR_LABEL: "${{ contains(github.event.pull_request.labels.*.name, 'chart: minor') }}" + MAJOR_LABEL: "${{ contains(github.event.pull_request.labels.*.name, 'chart: major') }}" + run: | + current_ver=$(cat "charts/plex-media-server/Chart.yaml" | grep 'version:' | cut -d' ' -f2) + + BUMP_TYPE='' + if [[ $MAJOR_LABEL == 'true' ]]; + then + BUMP_TYPE='major' + elif [[ $MINOR_LABEL == 'true' ]]; + then + BUMP_TYPE='minor' + elif [[ $PATCH_LABEL == 'true' ]]; + then + BUMP_TYPE='patch' + else + echo "::error title=Missing Label::Missing the chart versioning label" + exit -1 + fi + + echo "running ${BUMP_TYPE} version bump" + new_ver=$(semver bump "${BUMP_TYPE}" "$current_ver") + echo "last=${current_ver}" >> "$GITHUB_OUTPUT" + echo "new=${new_ver}" >> "$GITHUB_OUTPUT" + + - name: Update Chart Version + if: ${{ steps.check_bump.outputs.needsBump == 'true' }} + shell: bash + run: | + sed -i "s/version: ${{ steps.get_version.outputs.last }}/version: ${{ steps.get_version.outputs.new }}/g" "charts/plex-media-server/Chart.yaml" + + - name: Update Docs + shell: bash + run: | + helm-docs --chart-search-root charts --chart-to-generate charts/plex-media-server + + - name: Commit Changes + env: + message: ${{ steps.check_bump.outputs.needsBump == 'true' && format('to v{0}', steps.get_version.outputs.new) || 'documentation' }} + run: | + set +e + git config user.email '<>' + git config user.name github-actions + if ! git diff-index --quiet HEAD; then + git commit -m "chore: update $APP chart $message" --all + git push origin "HEAD:$GITHUB_HEAD_REF" + fi + lint-test: runs-on: ubuntu-latest steps: diff --git a/charts/plex-media-server/Chart.yaml b/charts/plex-media-server/Chart.yaml index 0381637c..7e1389ab 100644 --- a/charts/plex-media-server/Chart.yaml +++ b/charts/plex-media-server/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 name: plex-media-server -description: A Helm chart for deploying a PMS server to a kubernetes cluster +description: A Helm chart for deploying a PMS server to a Kubernetes cluster keywords: - plex @@ -22,7 +22,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.7.1 +version: 0.7.2 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/plex-media-server/README.md b/charts/plex-media-server/README.md index 7ee3fc1c..1721448b 100644 --- a/charts/plex-media-server/README.md +++ b/charts/plex-media-server/README.md @@ -1,7 +1,10 @@ -# plex-media-server Chart -=========== +# plex-media-server -A Helm chart for deploying the Plex Personal Media Server(PMS) server. +![Version: 0.7.2](https://img.shields.io/badge/Version-0.7.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.41.3](https://img.shields.io/badge/AppVersion-1.41.3-informational?style=flat-square) + +**Homepage:** + +A Helm chart for deploying a PMS server to a Kubernetes cluster While Plex is responsible for maintaining this Helm chart, we cannot provide support for troubleshooting related to its usage. For community assistance, please visit our [support forums](https://forums.plex.tv/). @@ -88,68 +91,64 @@ Before contributing, please read the [Code of Conduct](../../CODE_OF_CONDUCT.md) [GNU GPLv3](./LICENSE) -## Configuration - -The following table lists the configurable parameters of the Plex-media-server chart and their default values. - -| Parameter | Description | Default | -| ------------------------ | ----------------------- | -------------- | -| `image.registry` | | `"index.docker.io"` | -| `image.repository` | | `"plexinc/pms-docker"` | -| `image.tag` | | `"latest"` | -| `image.sha` | | `""` | -| `image.pullPolicy` | | `"IfNotPresent"` | -| `global.imageRegistry` | | `""` | -| `ingress.enabled` | | `false` | -| `ingress.ingressClassName` | | `"ingress-nginx"` | -| `ingress.url` | | `""` | -| `ingress.annotations` | | `{}` | -| `pms.storageClassName` | | `null` | -| `pms.configStorage` | | `"2Gi"` | -| `pms.configExistingClaim` | | `""` | -| `pms.gpu.nvidia.enabled` | | `false` | -| `pms.resources` | | `{}` | -| `pms.livenessProbe` | | `{}` | -| `pms.readinessProbe` | | `{}` | -| `initContainer.image.registry` | | `"index.docker.io"` | -| `initContainer.image.repository` | | `"alpine"` | -| `initContainer.image.tag` | | `"3.18.0"` | -| `initContainer.image.sha` | | `""` | -| `initContainer.image.pullPolicy` | | `"IfNotPresent"` | -| `initContainer.script` | | `""` | -| `runtimeClassName` | | `""` | -| `rclone.enabled` | | `false` | -| `rclone.image.registry` | | `"index.docker.io"` | -| `rclone.image.repository` | | `"rclone/rclone"` | -| `rclone.image.tag` | | `"1.62.2"` | -| `rclone.image.sha` | | `""` | -| `rclone.image.pullPolicy` | | `"IfNotPresent"` | -| `rclone.configSecret` | | `""` | -| `rclone.remotes` | | `[]` | -| `rclone.readOnly` | | `true` | -| `rclone.additionalArgs` | | `[]` | -| `rclone.resources` | | `{}` | -| `imagePullSecrets` | | `[]` | -| `nameOverride` | | `""` | -| `fullnameOverride` | | `""` | -| `serviceAccount.create` | | `true` | -| `serviceAccount.automountServiceAccountToken` | | `false` | -| `serviceAccount.annotations` | | `{}` | -| `serviceAccount.name` | | `""` | -| `statefulSet.annotations` | | `{}` | -| `statefulSet.podAnnotations` | | `{}` | -| `service.type` | | `"ClusterIP"` | -| `service.port` | | `32400` | -| `service.annotations` | | `{}` | -| `nodeSelector` | | `{}` | -| `tolerations` | | `[]` | -| `affinity` | | `{}` | -| `priorityClassName` | | `""` | -| `commonLabels` | | `{}` | -| `extraEnv` | | `{}` | -| `extraVolumeMounts` | | `[]` | -| `extraVolumes` | | `[]` | -| `extraContainers` | | `[]` | - ---- -_Documentation generated by [Frigate](https://frigate.readthedocs.io)._ +## Source Code + +* + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | | +| commonLabels | object | `{}` | Common Labels for all resources created by this chart. | +| extraContainers | list | `[]` | | +| extraEnv | object | `{}` | | +| extraVolumeMounts | list | `[]` | Optionally specify additional volume mounts for the PMS and init containers. | +| extraVolumes | list | `[]` | Optionally specify additional volumes for the pod. | +| fullnameOverride | string | `""` | | +| global.imageRegistry | string | `""` | Allow parent charts to override registry hostname | +| image | object | `{"pullPolicy":"IfNotPresent","registry":"index.docker.io","repository":"plexinc/pms-docker","sha":"","tag":"latest"}` | The docker image information for the pms application | +| image.registry | string | `"index.docker.io"` | The public dockerhub registry | +| image.tag | string | `"latest"` | If unset use "latest" | +| imagePullSecrets | list | `[]` | | +| ingress.annotations | object | `{}` | Custom annotations to put on the ingress resource | +| ingress.enabled | bool | `false` | Specify if an ingress resource for the pms server should be created or not | +| ingress.ingressClassName | string | `"ingress-nginx"` | The ingress class that should be used | +| ingress.url | string | `""` | The url to use for the ingress reverse proxy to point at this pms instance | +| initContainer | object | `{"image":{"pullPolicy":"IfNotPresent","registry":"index.docker.io","repository":"alpine","sha":"","tag":"3.18.0"},"script":""}` | A basic image that will convert the configmap to a file in the rclone config volume this is ignored if rclone is not enabled | +| initContainer.image.registry | string | `"index.docker.io"` | The public dockerhub registry | +| initContainer.image.tag | string | `"3.18.0"` | If unset use latest | +| initContainer.script | string | `""` | A custom script that will be run in an init container to do any setup before the PMS service starts up This will be run every time the pod starts, make sure that some mechanism is included to prevent this from running more than once if it should only be run on the first startup. | +| nameOverride | string | `""` | | +| nodeSelector | object | `{}` | | +| pms.configExistingClaim | string | `""` | Name of an existing `PersistentVolumeClaim` for the PMS database NOTE: When set, 'configStorage' and 'storageClassName' are ignored. | +| pms.configStorage | string | `"2Gi"` | The volume size to provision for the PMS database | +| pms.gpu.nvidia.enabled | bool | `false` | | +| pms.livenessProbe | object | `{}` | Add kubernetes liveness probe to pms container. | +| pms.readinessProbe | object | `{}` | Add kubernetes readiness probe to pms container. | +| pms.resources | object | `{}` | | +| pms.storageClassName | string | `nil` | The storage class to use when provisioning the pms config volume this needs to be created manually, null will use the default | +| priorityClassName | string | `""` | | +| rclone | object | `{"additionalArgs":[],"configSecret":"","enabled":false,"image":{"pullPolicy":"IfNotPresent","registry":"index.docker.io","repository":"rclone/rclone","sha":"","tag":"1.62.2"},"readOnly":true,"remotes":[],"resources":{}}` | The settings specific to rclone | +| rclone.additionalArgs | list | `[]` | Additional arguments to give to rclone when mounting the volume | +| rclone.configSecret | string | `""` | The name of the secret that contains the rclone configuration file. The rclone config key must be called `rclone.conf` in the secret All keys in configSecret will be available in /etc/rclone/. This might be useful if other files are needed, such as a private key for sftp mode. | +| rclone.enabled | bool | `false` | If the rclone sidecar should be created | +| rclone.image | object | `{"pullPolicy":"IfNotPresent","registry":"index.docker.io","repository":"rclone/rclone","sha":"","tag":"1.62.2"}` | The rclone image that should be used | +| rclone.image.registry | string | `"index.docker.io"` | The public dockerhub registry | +| rclone.image.tag | string | `"1.62.2"` | If unset use latest | +| rclone.readOnly | bool | `true` | If the remote volumes should be mounted as read only | +| rclone.remotes | list | `[]` | The remote drive that should be mounted using rclone this must be in the form of `name:[/optional/path]` this remote will be mounted at `/data/name` in the PMS container | +| runtimeClassName | string | `""` | Specify your own runtime class name eg use gpu | +| service.annotations | object | `{}` | Optional extra annotations to add to the service resource | +| service.port | int | `32400` | | +| service.type | string | `"ClusterIP"` | | +| serviceAccount.annotations | object | `{}` | Annotations to add to the service account | +| serviceAccount.automountServiceAccountToken | bool | `false` | If the service account token should be auto mounted | +| serviceAccount.create | bool | `true` | Specifies whether a service account should be created | +| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the fullname template | +| statefulSet.annotations | object | `{}` | Optional extra annotations to add to the service resource | +| statefulSet.podAnnotations | object | `{}` | Optional extra annotations to add to the pods in the statefulset | +| tolerations | list | `[]` | | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/charts/plex-media-server/README.md.gotmpl b/charts/plex-media-server/README.md.gotmpl new file mode 100644 index 00000000..a2188e1e --- /dev/null +++ b/charts/plex-media-server/README.md.gotmpl @@ -0,0 +1,104 @@ +{{ template "chart.header" . }} +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.badgesSection" . }} + +{{ template "chart.homepageLine" . }} + +{{ template "chart.description" . }} + + +While Plex is responsible for maintaining this Helm chart, we cannot provide support for troubleshooting related to its usage. For community assistance, please visit our [support forums](https://forums.plex.tv/). + +### Installation via Helm + +1. Add the Helm chart repo + +```bash +helm repo add plex https://raw.githubusercontent.com/plexinc/pms-docker/gh-pages +``` + +2. Inspect & modify the default values (optional) + +```bash +helm show values plex/plex-media-server > values.yaml +``` + +3. Install the chart + +```bash +helm upgrade --install plex plex/plex-media-server --values values.yaml +``` + +[Additional details available here](https://www.plex.tv/blog/plex-pro-week-23-a-z-on-k8s-for-plex-media-server/) + +### Sample init Container scripts + +If you already have a different PMS server running elsewhere and wish to migrate it to be running in Kubernetes +the easiest way to do that is to import the existing PMS database through the use of a custom init script. + +**Note: the init script must include a mechanism to exit early if the pms database already exists to prevent from overwriting its contents** + +The following script is an example (using the default `alpine` init container) that will pull +a tar gziped file that contains the pms `Library` directory from some web server. + +```sh +#!/bin/sh +echo "fetching pre-existing pms database to import..." + +if [ -d "/config/Library" ]; then + echo "PMS library already exists, exiting." + exit 0 +fi + +apk --no-cache add curl +curl http://example.com/pms.tgz -o pms.tgz +tar -xvzf pms.tgz -C /config +rm pms.tgz + +echo "Done." +``` + +This next example could be used if you don't have or can't host the existing pms database archive on a web server. +However, this one _does_ require that two commands are run manually once the init container starts up. + +1. Manually copy the pms database into the pod: `kubectl cp pms.tgz /:/pms.tgz.up -c -pms-chart-pms-init` +2. Once the file is uploaded copy rename it on the pod to the correct name that will be processed `kubectl exec -n --stdin --tty -c -pms-chart-pms-init h -- mv /pms.tgz.up /pms.tgz` + +The file is being uploaded with a temporary name so that the script does not start trying to unpack the database until it has finished uploading. + +```sh +#!/bin/sh +echo "waiting for pre-existing pms database to uploaded..." + +if [ -d "/config/Library" ]; then + echo "PMS library already exists, exiting." + exit 0 +fi + +# wait for the database archive to be manually copied to the server +while [ ! -f /pms.tgz ]; do sleep 2; done; + +tar -xvzf /pms.tgz -C /config +rm pms.tgz + +echo "Done." +``` + +## Contributing + +Before contributing, please read the [Code of Conduct](../../CODE_OF_CONDUCT.md). + +## License + +[GNU GPLv3](./LICENSE) + +{{ template "chart.maintainersSection" . }} + +{{ template "chart.sourcesSection" . }} + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +{{ template "helm-docs.versionFooter" . }} diff --git a/charts/plex-media-server/values.yaml b/charts/plex-media-server/values.yaml index ed486a3a..abd48b38 100644 --- a/charts/plex-media-server/values.yaml +++ b/charts/plex-media-server/values.yaml @@ -1,38 +1,39 @@ -# The docker image information for the pms application +# -- The docker image information for the pms application image: + # -- The public dockerhub registry registry: index.docker.io repository: plexinc/pms-docker - # If unset use "latest" + # -- If unset use "latest" tag: "latest" sha: "" pullPolicy: IfNotPresent global: - # Allow parent charts to override registry hostname + # -- Allow parent charts to override registry hostname imageRegistry: "" ingress: - # Specify if an ingress resource for the pms server should be created or not + # -- Specify if an ingress resource for the pms server should be created or not enabled: false - # The ingress class that should be used + # -- The ingress class that should be used ingressClassName: "ingress-nginx" - # The url to use for the ingress reverse proxy to point at this pms instance + # -- The url to use for the ingress reverse proxy to point at this pms instance url: "" - # Custom annotations to put on the ingress resource + # -- Custom annotations to put on the ingress resource annotations: {} pms: - # The storage class to use when provisioning the pms config volume + # -- The storage class to use when provisioning the pms config volume # this needs to be created manually, null will use the default storageClassName: null - # the volume size to provision for the PMS database + # -- The volume size to provision for the PMS database configStorage: 2Gi - # Name of an existing `PersistentVolumeClaim` for the PMS database + # -- Name of an existing `PersistentVolumeClaim` for the PMS database # NOTE: When set, 'configStorage' and 'storageClassName' are ignored. configExistingClaim: "" @@ -54,7 +55,7 @@ pms: # cpu: 100m # memory: 128Mi - # Add kubernetes liveness probe to pms container. + # -- Add kubernetes liveness probe to pms container. livenessProbe: {} # httpGet: # path: /identity @@ -64,7 +65,7 @@ pms: # timeoutSeconds: 1 # failureThreshold: 3 - # Add kubernetes readiness probe to pms container. + # -- Add kubernetes readiness probe to pms container. readinessProbe: {} # httpGet: # path: /identity @@ -74,19 +75,20 @@ pms: # timeoutSeconds: 1 # failureThreshold: 3 -# A basic image that will convert the configmap to a file in the rclone config volume +# -- A basic image that will convert the configmap to a file in the rclone config volume # this is ignored if rclone is not enabled initContainer: image: + # -- The public dockerhub registry registry: index.docker.io repository: alpine - # If unset use latest + # -- If unset use latest tag: 3.18.0 sha: "" pullPolicy: IfNotPresent - # A custom script that will be run in an init container to do any setup before the PMS service starts up - # This will be run everytime the pod starts, make sure that some mechanism is included to prevent + # -- A custom script that will be run in an init container to do any setup before the PMS service starts up + # This will be run every time the pod starts, make sure that some mechanism is included to prevent # this from running more than once if it should only be run on the first startup. script: "" ### @@ -109,24 +111,25 @@ initContainer: # # echo "Done." -# specify your own runtime class name eg use gpu +# -- Specify your own runtime class name eg use gpu runtimeClassName: "" -# the settings specific to rclone +# -- The settings specific to rclone rclone: - # if the rclone sidecar should be created + # -- If the rclone sidecar should be created enabled: false - # the rclone image that should be used + # -- The rclone image that should be used image: + # -- The public dockerhub registry registry: index.docker.io repository: rclone/rclone - # If unset use latest + # -- If unset use latest tag: 1.62.2 sha: "" pullPolicy: IfNotPresent - # The name of the secret that contains the rclone configuration file. + # -- The name of the secret that contains the rclone configuration file. # The rclone config key must be called `rclone.conf` in the secret # # All keys in configSecret will be available in /etc/rclone/. This might @@ -134,15 +137,15 @@ rclone: configSecret: "" - # the remote drive that should be mounted using rclone + # -- The remote drive that should be mounted using rclone # this must be in the form of `name:[/optional/path]` # this remote will be mounted at `/data/name` in the PMS container remotes: [] - # if the remote volumes should be mounted as read only + # -- If the remote volumes should be mounted as read only readOnly: true - # additional arguments to give to rclone when mounting the volume + # -- Additional arguments to give to rclone when mounting the volume additionalArgs: [] resources: {} @@ -162,20 +165,20 @@ nameOverride: "" fullnameOverride: "" serviceAccount: - # Specifies whether a service account should be created + # -- Specifies whether a service account should be created create: true - # If the service account token should be auto mounted + # -- If the service account token should be auto mounted automountServiceAccountToken: false - # Annotations to add to the service account + # -- Annotations to add to the service account annotations: {} - # The name of the service account to use. + # -- The name of the service account to use. # If not set and create is true, a name is generated using the fullname template name: "" statefulSet: - # optional extra annotations to add to the service resource + # -- Optional extra annotations to add to the service resource annotations: {} - # optional extra annotations to add to the pods in the statefulset + # -- Optional extra annotations to add to the pods in the statefulset podAnnotations: {} service: @@ -194,7 +197,7 @@ service: # https://access.redhat.com/solutions/7028639 # externalTrafficPolicy: Local - # optional extra annotations to add to the service resource + # -- Optional extra annotations to add to the service resource annotations: {} @@ -206,7 +209,7 @@ affinity: {} priorityClassName: "" -# Common Labels for all resources created by this chart. +# -- Common Labels for all resources created by this chart. commonLabels: {} extraEnv: {} @@ -224,13 +227,13 @@ extraEnv: {} # ALLOWED_NETWORKS: "0.0.0.0/0" -# Optionally specify additional volume mounts for the PMS and init containers. +# -- Optionally specify additional volume mounts for the PMS and init containers. extraVolumeMounts: [] # extraVolumeMounts: # - name: dev-dri # mountPath: /dev/dri -# Optionally specify additional volumes for the pod. +# -- Optionally specify additional volumes for the pod. extraVolumes: [] # extraVolumes: # - name: dev-dri