فهرست منبع

Make init container for permissions more flexible and apply to qbit (#1077)

* Make init container for permissions more flexible and apply to qbit

* remove qbittorrent from `charts`

* Add upgrade_info to qbit

* fix paths
Stavros Kois 2 سال پیش
والد
کامیت
30be52a860

+ 2 - 2
.github/workflows/common_library_tests.yaml

@@ -3,8 +3,8 @@ name: Common Library Tests
 on:
   pull_request:
     paths:
-      - library/common/
-      - library/common-test/
+      - library/common/**
+      - library/common-test/**
       - .github/ct-install-config/common*.yaml
       - '!.github/ct-install-config/charts*'
       - .github/workflows/common_library_tests.yaml

+ 0 - 2
charts/qbittorent/.helmignore

@@ -1,2 +0,0 @@
-# Patterns to ignore when building packages.
-*.png

+ 0 - 6
charts/qbittorent/1.0.11/Chart.lock

@@ -1,6 +0,0 @@
-dependencies:
-- name: common
-  repository: file://../../../library/common/2207.0.0
-  version: 2207.0.0
-digest: sha256:f17f3d458ca0210a52e39da0dce35034e900b36f2040d4b19bed46a7aae91506
-generated: "2022-07-25T00:20:48.056303531+05:00"

+ 0 - 14
charts/qbittorent/1.0.11/Chart.yaml

@@ -1,14 +0,0 @@
-apiVersion: v2
-appVersion: 4.5.2
-dependencies:
-- name: common
-  repository: file://../../../library/common/2207.0.0
-  version: 2207.0.0
-description: Download data from qbittorent.
-home: https://www.qbittorrent.org/
-icon: https://github.com/linuxserver/docker-templates/raw/master/linuxserver.io/img/qbittorrent-icon.png
-keywords:
-- storage
-- torrent
-name: qbittorent
-version: 1.0.11

+ 0 - 3
charts/qbittorent/1.0.11/README.md

@@ -1,3 +0,0 @@
-# qbittorent
-
-[qbittorent](https://qbittorrent.com/) is a utorrent downloading client

+ 0 - 3
charts/qbittorent/1.0.11/app-readme.md

@@ -1,3 +0,0 @@
-# Qbittorent
-
-[qbittorent](https://qbittorrent.com/) is a utorrent downloading client.

BIN
charts/qbittorent/1.0.11/charts/common-2207.0.0.tgz


+ 0 - 4
charts/qbittorent/1.0.11/ix_values.yaml

@@ -1,4 +0,0 @@
-image:
-  pullPolicy: IfNotPresent
-  repository: linuxserver/qbittorrent
-  tag: 4.5.2

+ 0 - 251
charts/qbittorent/1.0.11/questions.yaml

@@ -1,251 +0,0 @@
-groups:
-  - name: "Configuration"
-    description: "Qbittorent application configuration"
-  - name: "Storage"
-    description: "Configure storage for qbittorent"
-  - name: "Networking"
-    description: "Networking Configuration for qbittorent"
-  - name: "Advanced DNS Settings"
-    description: "Configure DNS settings"
-  - name: "Resource Limits"
-    description: "Set CPU/memory limits for Kubernetes Pod"
-
-portals:
-  web_portal:
-    protocols:
-      - "http"
-    host:
-      - "$node_ip"
-    ports:
-      - "$variable-web_port"
-    path: "/"
-
-questions:
-  - variable: web_port
-    label: "Web Port for qbittorent"
-    group: Networking
-    schema:
-      type: int
-      min: 8000
-      max: 65535
-      default: 20909
-      required: true
-  - variable: tcp_port
-    label: "TCP Port for qbittorent"
-    group: Networking
-    schema:
-      type: int
-      min: 8000
-      max: 65535
-      default: 20988
-      required: true
-  - variable: udp_port
-    label: "UDP Port for qbittorent"
-    group: Networking
-    schema:
-      type: int
-      min: 8000
-      max: 65535
-      default: 20989
-      required: true
-
-  - variable: dnsConfig
-    label: "DNS Configuration"
-    group: "Advanced DNS Settings"
-    schema:
-      type: dict
-      attrs:
-        - variable: options
-          label: "DNS Options"
-          schema:
-            type: list
-            items:
-              - variable: optionsEntry
-                label: "Option Entry Configuration"
-                schema:
-                  type: dict
-                  attrs:
-                    - variable: name
-                      label: "Option Name"
-                      schema:
-                        type: string
-                        required: true
-                    - variable: value
-                      label: "Option Value"
-                      schema:
-                        type: string
-                        required: true
-
-  - variable: ownerUID
-    label: "Owner User ID"
-    group: Configuration
-    schema:
-      type: int
-      default: 568
-      min: 1
-      max: 65535
-
-  - variable: ownerGID
-    label: "Owner Group ID"
-    group: Configuration
-    schema:
-      type: int
-      default: 568
-      min: 1
-      max: 65535
-
-  - variable: timezone
-    label: "Timezone"
-    group: "Configuration"
-    schema:
-      type: string
-      $ref:
-        - "definitions/timezone"
-
-  - variable: environmentVariables
-    label: "Qbittorent environment"
-    group: "Configuration"
-    schema:
-      type: list
-      default: [ ]
-      items:
-        - variable: environmentVariable
-          label: "Environment Variable"
-          schema:
-            type: dict
-            attrs:
-              - variable: name
-                label: "Name"
-                schema:
-                  type: string
-              - variable: value
-                label: "Value"
-                schema:
-                  type: string
-
-  - variable: appVolumeMounts
-    label: "Qbittorent Storage"
-    group: "Storage"
-    schema:
-      type: dict
-      attrs:
-        - variable: config
-          label: "Configuration Volume"
-          schema:
-            type: dict
-            attrs:
-              - variable: datasetName
-                label: "Configuration Volume Dataset Name"
-                schema:
-                  type: string
-                  hidden: true
-                  $ref:
-                    - "normalize/ixVolume"
-                  show_if: [["hostPathEnabled", "=", false]]
-                  default: "ix-qbittorent_config"
-                  editable: false
-              - variable: mountPath
-                label: "Configuration Mount Path"
-                description: "Path where the volume will be mounted inside the pod"
-                schema:
-                  type: path
-                  hidden: true
-                  editable: true
-                  default: "/config"
-              - variable: hostPathEnabled
-                label: "Enable Custom Host Path for Qbittorent Configuration Volume"
-                schema:
-                  type: boolean
-                  default: false
-                  show_subquestions_if: true
-                  subquestions:
-                    - variable: hostPath
-                      label: "Host Path for Qbittorent Configuration Volume"
-                      schema:
-                        type: hostpath
-                        required: true
-        - variable: downloads
-          label: "Downloads Volume"
-          schema:
-            type: dict
-            attrs:
-              - variable: datasetName
-                label: "Downloads Volume Dataset Name"
-                schema:
-                  type: string
-                  hidden: true
-                  $ref:
-                    - "normalize/ixVolume"
-                  show_if: [["hostPathEnabled", "=", false]]
-                  default: "ix-qbittorent_data"
-                  editable: false
-              - variable: mountPath
-                label: "Downloads Mount Path"
-                description: "Path where the volume will be mounted inside the pod"
-                schema:
-                  type: path
-                  hidden: true
-                  editable: true
-                  default: "/downloads"
-              - variable: hostPathEnabled
-                label: "Enable Custom Host Path for Qbittorent Downloads Volume"
-                schema:
-                  type: boolean
-                  default: false
-                  show_subquestions_if: true
-                  subquestions:
-                    - variable: hostPath
-                      label: "Host Path for Qbittorent Downloads Volume"
-                      schema:
-                        type: hostpath
-                        required: true
-
-  - variable: extraAppVolumeMounts
-    label: "Extra Host Path Volumes"
-    group: "Storage"
-    schema:
-      type: list
-      items:
-        - variable: extraAppVolume
-          label: "Host Path Volume"
-          description: "Add an extra host path volume for Qbittorent application"
-          schema:
-            type: dict
-            attrs:
-              - variable: mountPath
-                label: "Mount Path in Pod"
-                description: "Path where the volume will be mounted inside the pod"
-                schema:
-                  type: path
-                  required: true
-              - variable: hostPath
-                label: "Host Path"
-                description: "Host path"
-                schema:
-                  type: hostpath
-                  required: true
-
-  - variable: enableResourceLimits
-    label: "Enable Pod resource limits"
-    group: "Resource Limits"
-    schema:
-      type: boolean
-      default: false
-  - variable: cpuLimit
-    label: "CPU Limresource limitsit"
-    description: "CPU resource limit allow  plain integer values with suffix m(milli) e.g 1000m, 100."
-    group: "Resource Limits"
-    schema:
-      type: string
-      show_if: [["enableResourceLimits", "=", true]]
-      valid_chars: "^\\d+(?:\\.\\d+(?!.*m$)|m?$)"
-      default: "4000m"
-  - variable: memLimit
-    label: "Memory Limit"
-    group: "Resource Limits"
-    description: "Memory limits is specified by number of bytes. Followed by quantity suffix like E,P,T,G,M,k and Ei,Pi,Ti,Mi,Gi,Ki can also be used. e.g 129e6, 129M,  128974848000m, 123Mi"
-    schema:
-      type: string
-      show_if: [["enableResourceLimits", "=", true]]
-      valid_chars: "^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$"
-      default: "8Gi"

+ 0 - 62
charts/qbittorent/1.0.11/templates/deployment.yaml

@@ -1,62 +0,0 @@
-{{ include "common.storage.hostPathValidate" .Values }}
-apiVersion: {{ template "common.capabilities.deployment.apiVersion" . }}
-kind: Deployment
-metadata:
-  name: {{ template "common.names.fullname" . }}
-  labels:
-    app: {{ template "common.names.name" . }}
-    chart: {{ template "common.names.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
-  annotations:
-    rollme: {{ randAlphaNum 5 | quote }}
-spec:
-  replicas: {{ (default 1 .Values.replicas) }}
-  strategy:
-    type: "Recreate"
-  selector:
-    matchLabels:
-      app: {{ template "common.names.name" . }}
-      release: {{ .Release.Name }}
-  template:
-    metadata:
-      name: {{ template "common.names.fullname" . }}
-      labels:
-        app: {{ template "common.names.name" . }}
-        release: {{ .Release.Name }}
-        {{- include "common.labels.selectorLabels" . | nindent 8 }}
-      annotations: {{ include "common.annotations" . | nindent 8 }}
-    spec:
-      hostNetwork: false
-      containers:
-        - name: {{ .Chart.Name }}
-          {{ include "common.resources.limitation" . | nindent 10 }}
-          {{ include "common.containers.imageConfig" .Values.image | nindent 10 }}
-          volumeMounts: {{ include "common.storage.configureAppVolumeMountsInContainer" .Values | nindent 12 }}
-          {{ range $index, $hostPathConfiguration := .Values.extraAppVolumeMounts }}
-            - name: extrappvolume-{{ $index }}
-              mountPath: {{ $hostPathConfiguration.mountPath }}
-          {{ end }}
-          ports:
-            - name: web
-              containerPort: {{ .Values.web_port }}
-            - name: tcp
-              containerPort: {{ .Values.tcp_port }}
-              protocol: TCP
-            - name: udp
-              containerPort: {{ .Values.udp_port }}
-              protocol: UDP
-          env:
-            {{ $envList := (default list .Values.environmentVariables) }}
-            {{ $envList = mustAppend $envList (dict "name" "PUID" "value" (printf "%d" (.Values.ownerUID | int))) }}
-            {{ $envList = mustAppend $envList (dict "name" "PGID" "value" (printf "%d" (.Values.ownerGID | int))) }}
-            {{ $envList = mustAppend $envList (dict "name" "TZ" "value" (printf "%s" .Values.timezone)) }}
-            {{ $envList = mustAppend $envList (dict "name" "WEBUI_PORT" "value" (printf "%d" (.Values.web_port | int))) }}
-            {{ include "common.containers.environmentVariables" (dict "environmentVariables" $envList) | nindent 12 }}
-{{ include "common.networking.dnsConfiguration" .Values | nindent 6 }}
-      volumes: {{ include "common.storage.configureAppVolumes" .Values | nindent 8 }}
-      {{ range $index, $hostPathConfiguration := .Values.extraAppVolumeMounts }}
-        - name: extrappvolume-{{ $index }}
-          hostPath:
-            path: {{ $hostPathConfiguration.hostPath }}
-      {{ end }}

+ 0 - 33
charts/qbittorent/1.0.11/templates/pre-install-job.yaml

@@ -1,33 +0,0 @@
-apiVersion: batch/v1
-kind: Job
-metadata:
-  name: "{{ template "common.names.fullname" . }}-preinstall-job"
-  labels:
-    app.kubernetes.io/managed-by: {{ .Release.Service | quote }}
-    app.kubernetes.io/instance: {{ .Release.Name | quote }}
-    app.kubernetes.io/version: {{ .Chart.AppVersion }}
-    helm.sh/chart: {{ template "common.names.chart" . }}
-  annotations:
-    "helm.sh/hook": pre-install
-    "helm.sh/hook-delete-policy": hook-succeeded
-spec:
-  template:
-    metadata:
-      name: "{{ template "common.names.fullname" . }}-preinstall-hook"
-      labels:
-        app.kubernetes.io/managed-by: {{ .Release.Service | quote }}
-        app.kubernetes.io/instance: {{ .Release.Name | quote }}
-        helm.sh/chart: {{ template "common.names.chart" . }}
-    spec:
-      restartPolicy: Never
-      containers:
-      - name: pre-install-job
-        image: "alpine:latest"
-        command:
-          - "chown"
-          - "-R"
-          - "{{ .Values.ownerUID }}:{{ .Values.ownerGID }}"
-          - "{{ .Values.appVolumeMounts.config.mountPath }}"
-          - "{{ .Values.appVolumeMounts.downloads.mountPath }}"
-        volumeMounts: {{ include "common.storage.configureAppVolumeMountsInContainer" .Values | nindent 12 }}
-      volumes: {{ include "common.storage.configureAppVolumes" .Values | nindent 8 }}

+ 0 - 11
charts/qbittorent/1.0.11/templates/service.yaml

@@ -1,11 +0,0 @@
-{{ $selectors := list }}
-{{ $selectors = mustAppend $selectors (dict "key" "app" "value" (include "common.names.name" .) ) }}
-{{ $selectors = mustAppend $selectors (dict "key" "release" "value" .Release.Name ) }}
-{{ $ports := list }}
-{{ $ports = mustAppend $ports (dict "name" "web" "port" .Values.web_port "nodePort" .Values.web_port "targetPort" .Values.web_port) }}
-{{ $ports = mustAppend $ports (dict "name" "tcp" "port" .Values.tcp_port "nodePort" .Values.tcp_port "targetPort" .Values.tcp_port) }}
-{{ $ports = mustAppend $ports (dict "name" "udp" "port" .Values.udp_port "nodePort" .Values.udp_port "targetPort" .Values.udp_port "protocol" "UDP") }}
-{{ $params := . }}
-{{ $_ := set $params "commonService" (dict "type" "NodePort" "ports" $ports ) }}
-{{ $_1 := set .Values "extraSelectorLabels" $selectors }}
-{{ include "common.classes.service" $params }}

+ 0 - 21
charts/qbittorent/1.0.11/templates/tests/deployment-check.yaml

@@ -1,21 +0,0 @@
-{{- $serviceName := (include "common.names.fullname" .) -}}
-apiVersion: v1
-kind: Pod
-metadata:
-  name: {{ .Release.Name }}-qbittorent
-  labels:
-    app: {{ .Release.Name }}
-    release: {{ .Release.Name }}
-  annotations:
-    "helm.sh/hook": test
-spec:
-    containers:
-      - name: test-curl
-        image: alpine/curl
-        imagePullPolicy: "IfNotPresent"
-        command:
-          - /bin/sh
-          - -ec
-          - |
-            curl --connect-timeout 5 --max-time 10 --retry 5 --retry-delay 15 --retry-max-time 90 --retry-all-errors -ksf http://{{ $serviceName }}:{{.Values.web_port}}/
-    restartPolicy: Never

+ 0 - 22
charts/qbittorent/1.0.11/test_values.yaml

@@ -1,22 +0,0 @@
-appVolumeMounts:
-  config:
-    emptyDir: true
-    mountPath: /config
-  downloads:
-    emptyDir: true
-    mountPath: /downloads
-dnsConfig:
-  options: []
-emptyDirVolumes: true
-environmentVariables: []
-extraAppVolumeMounts: []
-image:
-  pullPolicy: IfNotPresent
-  repository: linuxserver/qbittorrent
-  tag: 4.5.2
-ownerGID: 568
-ownerUID: 568
-tcp_port: 32001
-timezone: Europe/London
-udp_port: 32002
-web_port: 32000

+ 0 - 4
charts/qbittorent/item.yaml

@@ -1,4 +0,0 @@
-icon_url: https://github.com/linuxserver/docker-templates/raw/master/linuxserver.io/img/qbittorrent-icon.png
-categories:
-- media
-- torrent

+ 0 - 1
charts/qbittorent/upgrade_info.json

@@ -1 +0,0 @@
-{"filename":  "ix_values.yaml", "keys": ["image"], "test_filename": "test_values.yaml"}

+ 0 - 31
charts/qbittorent/upgrade_strategy

@@ -1,31 +0,0 @@
-#!/usr/bin/python3
-import json
-import re
-import sys
-
-from catalog_update.upgrade_strategy import semantic_versioning
-
-
-RE_STABLE_VERSION = re.compile(r'[0-9]\.[0-9]\.[0-9]')
-
-
-def newer_mapping(image_tags):
-    key = list(image_tags.keys())[0]
-    tags = {t: t for t in image_tags[key] if RE_STABLE_VERSION.fullmatch(t)}
-    version = semantic_versioning(list(tags))
-    if not version:
-        return {}
-
-    return {
-        'tags': {key: tags[version]},
-        'app_version': version,
-    }
-
-
-if __name__ == '__main__':
-    try:
-        versions_json = json.loads(sys.stdin.read())
-    except ValueError:
-        raise ValueError('Invalid json specified')
-
-    print(json.dumps(newer_mapping(versions_json)))

+ 1 - 1
library/common/Chart.yaml

@@ -2,7 +2,7 @@ apiVersion: v2
 name: common
 description: A library chart for iX Official Catalog
 type: library
-version: 1.0.1
+version: 1.0.2
 appVersion: v1
 annotations:
   title: Common Library Chart

+ 38 - 6
library/common/templates/app_functions/_permissions.tpl

@@ -9,9 +9,15 @@ GID: GID to change permissions to
 {{- define "ix.v1.common.app.permissions" -}}
   {{- $type := .type | default "install" -}}
   {{- $containerName := .containerName | default "permissions" -}}
+  {{- $mode := .mode | default "always" -}}
   {{- $UID := .UID -}}
   {{- $GID := .GID -}}
 
+  {{- $modes := (list "always" "check") -}}
+  {{- if not (mustHas $mode $modes) -}}
+    {{- fail (printf "Permissions Container - [mode] must be one of [%s]" (join ", " $modes)) -}}
+  {{- end -}}
+
   {{- if (kindIs "invalid" $type) -}}
     {{- fail "Permissions Container - [type] cannot be empty" -}}
   {{- end -}}
@@ -45,10 +51,36 @@ GID: GID to change permissions to
   args:
     - -c
     - |
-      echo "Changing ownership to {{ $UID }}:{{ $GID }} on the following directories:"
-      ls -la /mnt/directories
-      chown -R {{ $UID }}:{{ $GID }} /mnt/directories
-      echo "Finished changing ownership"
-      echo "Permissions after changing ownership:"
-      ls -la /mnt/directories
+      for dir in /mnt/directories/*; do
+        if [ ! -d "$dir" ]; then
+          echo "[$dir] is not a directory, skipping"
+          continue
+        fi
+
+        echo "Current Permissions on ["$dir"]:"
+        stat -c "%u %g" "$dir"
+
+      {{- if eq $mode "check" }} {{/* If mode is check, check parent dir */}}
+        if [ $(stat -c %u "$dir") -eq {{ $UID }} ] && [ $(stat -c %g "$dir") -eq {{ $GID }} ]; then
+          echo "Permissions are correct. Skipping..."
+          fix_perms="false"
+        else
+          echo "Permissions are incorrect. Fixing..."
+          fix_perms="true"
+        fi
+
+      {{- else if eq $mode "always" }} {{/* If mode is always, always fix perms */}}
+
+        fix_perms="true"
+
+      {{- end }}
+
+        if [ "$fix_perms" = "true" ]; then
+          echo "Changing ownership to {{ $UID }}:{{ $GID }} on: ["$dir"]"
+          chown -R {{ $UID }}:{{ $GID }} "$dir"
+          echo "Finished changing ownership"
+          echo "Permissions after changing ownership:"
+          stat -c "%u %g" "$dir"
+        fi
+      done
 {{- end -}}

+ 3 - 3
library/ix-dev/community/qbittorrent/Chart.lock

@@ -1,6 +1,6 @@
 dependencies:
 - name: common
   repository: file://../../../common
-  version: 1.0.1
-digest: sha256:ec8784f128039af68613a8268208bde360d5b178f811c4a16c79a1650ca8be92
-generated: "2023-04-03T17:54:32.688255209+03:00"
+  version: 1.0.2
+digest: sha256:c6d163d47f4544126093e26c4f13a6f5a35feeaa594df31459e9fb31dcb2c938
+generated: "2023-04-04T07:09:23.956314889Z"

+ 2 - 2
library/ix-dev/community/qbittorrent/Chart.yaml

@@ -3,7 +3,7 @@ description: The qBittorrent project aims to provide an open-source software alt
 annotations:
   title: qBittorrent
 type: application
-version: 1.0.0
+version: 1.0.1
 apiVersion: v2
 appVersion: '4.5.2'
 kubeVersion: '>=1.16.0-0'
@@ -13,7 +13,7 @@ maintainers:
 dependencies:
 - name: common
   repository: file://../../../common
-  version: 1.0.1
+  version: 1.0.2
 home: https://www.qbittorrent.org/
 icon: https://upload.wikimedia.org/wikipedia/commons/9/9e/Qbittorrent_logo.png
 sources:

BIN
library/ix-dev/community/qbittorrent/charts/common-1.0.1.tgz


BIN
library/ix-dev/community/qbittorrent/charts/common-1.0.2.tgz


+ 7 - 39
library/ix-dev/community/qbittorrent/templates/_qbittorrent.tpl

@@ -40,43 +40,11 @@ workload:
               port: "{{ .Values.qbitNetwork.webPort }}"
               path: /
       initContainers:
-        check-permissions:
-          enabled: true
-          type: init
-          imageSelector: bashImage
-          resources:
-            limits:
-              cpu: 1000m
-              memory: 512Mi
-          securityContext:
-            runAsUser: 0
-            runAsGroup: 0
-            runAsNonRoot: false
-            readOnlyRootFilesystem: false
-            capabilities:
-              add:
-                - CHOWN
-          command: bash
-          args:
-            - -c
-            - |
-              for dir in /mnt/directories/*; do
-                if [ ! -d "$dir" ]; then
-                  echo "[$dir] is not a directory, skipping"
-                  continue
-                fi
-
-                if [ $(stat -c %u "$dir") -eq {{ .Values.qbitRunAs.user }} ] && [ $(stat -c %g "$dir") -eq {{ .Values.qbitRunAs.group }} ]; then
-                  echo "Permissions on ["$dir"] are correct"
-                else
-                  echo "Permissions on ["$dir"] are incorrect"
-                  echo "Changing ownership to {{ .Values.qbitRunAs.user }}:{{ .Values.qbitRunAs.group }} on the following directories: ["$dir"]"
-                  chown -R {{ .Values.qbitRunAs.user }}:{{ .Values.qbitRunAs.group }} "$dir"
-                  echo "Finished changing ownership"
-                  echo "Permissions after changing ownership:"
-                  stat -c "%u %g" "$dir"
-                fi
-              done
+      {{- include "ix.v1.common.app.permissions" (dict "containerName" "01-permissions"
+                                                        "UID" .Values.qbitRunAs.user
+                                                        "GID" .Values.qbitRunAs.group
+                                                        "mode" "check"
+                                                        "type" "init") | nindent 8 }}
 
 {{/* Service */}}
 service:
@@ -122,7 +90,7 @@ persistence:
       qbittorrent:
         qbittorrent:
           mountPath: /config
-        check-permissions:
+        01-permissions:
           mountPath: /mnt/directories/config
   downloads:
     enabled: true
@@ -133,6 +101,6 @@ persistence:
       qbittorrent:
         qbittorrent:
           mountPath: /downloads
-        check-permissions:
+        01-permissions:
           mountPath: /mnt/directories/downloads
 {{- end -}}

+ 1 - 0
library/ix-dev/community/qbittorrent/upgrade_info.json

@@ -0,0 +1 @@
+{"filename": "values.yaml", "keys": ["image"]}