diff --git a/charts/gohoarder/Chart.yaml b/charts/gohoarder/Chart.yaml index c2ee078..f74895e 100644 --- a/charts/gohoarder/Chart.yaml +++ b/charts/gohoarder/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: gohoarder description: A universal package cache proxy supporting npm, PyPI, and Go modules with security scanning type: application -version: 0.1.10 -appVersion: "0.1.10" +version: 0.1.13 +appVersion: "0.1.13" keywords: - package-manager - cache diff --git a/charts/gohoarder/templates/_helpers.tpl b/charts/gohoarder/templates/_helpers.tpl index 859f65a..d4585a0 100644 --- a/charts/gohoarder/templates/_helpers.tpl +++ b/charts/gohoarder/templates/_helpers.tpl @@ -181,7 +181,7 @@ Validate SQLite configuration - SQLite cannot be used with SMB/NFS network stora {{- if .Values.metadata.sqlite.persistence.enabled }} {{- $storageClass := .Values.metadata.sqlite.persistence.storageClass | default .Values.storage.storageClass }} {{- if or (contains "smb" ($storageClass | lower)) (contains "cifs" ($storageClass | lower)) (contains "nfs" ($storageClass | lower)) }} - {{- fail "\n\n❌ ERROR: SQLite cannot be used with SMB/CIFS/NFS network storage!\n\nSQLite requires POSIX file locking which is not reliably supported over network filesystems.\nThis will cause 'database is locked' errors and data corruption.\n\nPlease choose ONE of the following solutions:\n\n1. Use PostgreSQL for network storage (RECOMMENDED for production):\n metadata:\n backend: postgresql\n postgresql:\n host: your-postgres-host\n ...\n\n2. Use local storage for SQLite (OK for development):\n metadata:\n sqlite:\n persistence:\n enabled: true\n storageClass: local-path # or another local storage class\n\n3. Disable persistence (data will be lost on pod restart):\n metadata:\n sqlite:\n persistence:\n enabled: false\n\nFor more information, see: https://www.sqlite.org/lockingv3.html\n" }} + {{- fail "\n\n❌ ERROR: SQLite cannot be used with SMB/CIFS/NFS network storage!\n\nSQLite requires POSIX file locking which is not reliably supported over network filesystems.\nThis will cause 'database is locked' errors and data corruption.\n\nPlease choose ONE of the following solutions:\n\n1. Use PostgreSQL for network storage (RECOMMENDED for production):\n metadata:\n backend: postgresql\n postgresql:\n host: your-postgres-host\n ...\n\n2. Use MySQL/MariaDB for network storage (alternative to PostgreSQL):\n metadata:\n backend: mysql\n mysql:\n host: your-mysql-host\n ...\n\n3. Use local storage for SQLite (OK for development):\n metadata:\n sqlite:\n persistence:\n enabled: true\n storageClass: local-path # or another local storage class\n\n4. Disable persistence (data will be lost on pod restart):\n metadata:\n sqlite:\n persistence:\n enabled: false\n\nFor more information, see: https://www.sqlite.org/lockingv3.html\n" }} {{- end }} {{- end }} {{- end }} diff --git a/charts/gohoarder/templates/deployment-scanner.yaml b/charts/gohoarder/templates/deployment-scanner.yaml index d0fbe92..308db42 100644 --- a/charts/gohoarder/templates/deployment-scanner.yaml +++ b/charts/gohoarder/templates/deployment-scanner.yaml @@ -29,6 +29,77 @@ spec: serviceAccountName: {{ include "gohoarder.serviceAccountName" . }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} + {{- if .Values.migration.enabled }} + initContainers: + # Wait for database to be ready + - name: wait-for-db + image: busybox:1.36 + command: + - sh + - -c + - | + echo "Waiting for database..." + {{- if eq .Values.metadata.backend "postgresql" }} + until nc -z {{ .Values.metadata.postgresql.host }} {{ .Values.metadata.postgresql.port }}; do + echo " PostgreSQL not ready, retrying in 2s..." + sleep 2 + done + echo "✓ PostgreSQL is ready" + {{- else if eq .Values.metadata.backend "mysql" }} + until nc -z {{ .Values.metadata.mysql.host }} {{ .Values.metadata.mysql.port }}; do + echo " MySQL not ready, retrying in 2s..." + sleep 2 + done + echo "✓ MySQL is ready" + {{- else }} + echo "✓ SQLite (no wait needed)" + {{- end }} + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + runAsUser: 1000 + resources: + limits: + cpu: 100m + memory: 64Mi + requests: + cpu: 10m + memory: 32Mi + # Run database migrations + - name: migrate + image: "{{ .Values.migration.image.repository }}:{{ .Values.migration.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.migration.image.pullPolicy }} + env: + - name: DB_DRIVER + value: {{ .Values.metadata.backend | quote }} + {{- if eq .Values.metadata.backend "postgresql" }} + - name: DATABASE_URL + value: "postgresql://{{ .Values.metadata.postgresql.username }}:{{ .Values.metadata.postgresql.password }}@{{ .Values.metadata.postgresql.host }}:{{ .Values.metadata.postgresql.port }}/{{ .Values.metadata.postgresql.database }}?sslmode={{ .Values.metadata.postgresql.sslMode }}" + {{- else if eq .Values.metadata.backend "mysql" }} + - name: DATABASE_URL + value: "{{ .Values.metadata.mysql.username }}:{{ .Values.metadata.mysql.password }}@tcp({{ .Values.metadata.mysql.host }}:{{ .Values.metadata.mysql.port }})/{{ .Values.metadata.mysql.database }}?charset={{ .Values.metadata.mysql.charset }}&parseTime={{ .Values.metadata.mysql.parseTime }}" + {{- else }} + - name: DATABASE_URL + value: "/var/lib/gohoarder/metadata/gohoarder.db" + {{- end }} + args: + - --driver=$(DB_DRIVER) + - --dsn=$(DATABASE_URL) + - --action=migrate + - --log-level={{ .Values.migration.logLevel | default "info" }} + - --timeout={{ .Values.migration.timeout | default "5m" }} + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + runAsUser: 1000 + resources: + {{- toYaml .Values.migration.resources | nindent 10 }} + {{- if eq .Values.metadata.backend "sqlite" }} + volumeMounts: + - name: metadata + mountPath: /var/lib/gohoarder/metadata + {{- end }} + {{- end }} containers: - name: scanner securityContext: @@ -38,6 +109,52 @@ spec: env: - name: CONFIG_FILE value: /etc/gohoarder/config.yaml + {{- if and (eq .Values.metadata.backend "postgresql") .Values.metadata.postgresql.existingSecret }} + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: {{ .Values.metadata.postgresql.existingSecret }} + key: username + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.metadata.postgresql.existingSecret }} + key: password + {{- else if and (eq .Values.metadata.backend "postgresql") .Values.metadata.postgresql.username }} + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: {{ include "gohoarder.fullname" . }}-postgresql + key: username + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "gohoarder.fullname" . }}-postgresql + key: password + {{- end }} + {{- if and (or (eq .Values.metadata.backend "mysql") (eq .Values.metadata.backend "mariadb")) .Values.metadata.mysql.existingSecret }} + - name: MYSQL_USER + valueFrom: + secretKeyRef: + name: {{ .Values.metadata.mysql.existingSecret }} + key: username + - name: MYSQL_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.metadata.mysql.existingSecret }} + key: password + {{- else if and (or (eq .Values.metadata.backend "mysql") (eq .Values.metadata.backend "mariadb")) .Values.metadata.mysql.username }} + - name: MYSQL_USER + valueFrom: + secretKeyRef: + name: {{ include "gohoarder.fullname" . }}-mysql + key: username + - name: MYSQL_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "gohoarder.fullname" . }}-mysql + key: password + {{- end }} {{- if and .Values.security.scanners.ghsa.enabled .Values.security.scanners.ghsa.existingSecret }} - name: GHSA_TOKEN valueFrom: diff --git a/charts/gohoarder/templates/deployment-server.yaml b/charts/gohoarder/templates/deployment-server.yaml index 92dd164..3cfb70c 100644 --- a/charts/gohoarder/templates/deployment-server.yaml +++ b/charts/gohoarder/templates/deployment-server.yaml @@ -30,6 +30,77 @@ spec: serviceAccountName: {{ include "gohoarder.serviceAccountName" . }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} + {{- if .Values.migration.enabled }} + initContainers: + # Wait for database to be ready + - name: wait-for-db + image: busybox:1.36 + command: + - sh + - -c + - | + echo "Waiting for database..." + {{- if eq .Values.metadata.backend "postgresql" }} + until nc -z {{ .Values.metadata.postgresql.host }} {{ .Values.metadata.postgresql.port }}; do + echo " PostgreSQL not ready, retrying in 2s..." + sleep 2 + done + echo "✓ PostgreSQL is ready" + {{- else if eq .Values.metadata.backend "mysql" }} + until nc -z {{ .Values.metadata.mysql.host }} {{ .Values.metadata.mysql.port }}; do + echo " MySQL not ready, retrying in 2s..." + sleep 2 + done + echo "✓ MySQL is ready" + {{- else }} + echo "✓ SQLite (no wait needed)" + {{- end }} + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + runAsUser: 1000 + resources: + limits: + cpu: 100m + memory: 64Mi + requests: + cpu: 10m + memory: 32Mi + # Run database migrations + - name: migrate + image: "{{ .Values.migration.image.repository }}:{{ .Values.migration.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.migration.image.pullPolicy }} + env: + - name: DB_DRIVER + value: {{ .Values.metadata.backend | quote }} + {{- if eq .Values.metadata.backend "postgresql" }} + - name: DATABASE_URL + value: "postgresql://{{ .Values.metadata.postgresql.username }}:{{ .Values.metadata.postgresql.password }}@{{ .Values.metadata.postgresql.host }}:{{ .Values.metadata.postgresql.port }}/{{ .Values.metadata.postgresql.database }}?sslmode={{ .Values.metadata.postgresql.sslMode }}" + {{- else if eq .Values.metadata.backend "mysql" }} + - name: DATABASE_URL + value: "{{ .Values.metadata.mysql.username }}:{{ .Values.metadata.mysql.password }}@tcp({{ .Values.metadata.mysql.host }}:{{ .Values.metadata.mysql.port }})/{{ .Values.metadata.mysql.database }}?charset={{ .Values.metadata.mysql.charset }}&parseTime={{ .Values.metadata.mysql.parseTime }}" + {{- else }} + - name: DATABASE_URL + value: "/var/lib/gohoarder/metadata/gohoarder.db" + {{- end }} + args: + - --driver=$(DB_DRIVER) + - --dsn=$(DATABASE_URL) + - --action=migrate + - --log-level={{ .Values.migration.logLevel | default "info" }} + - --timeout={{ .Values.migration.timeout | default "5m" }} + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + runAsUser: 1000 + resources: + {{- toYaml .Values.migration.resources | nindent 10 }} + {{- if eq .Values.metadata.backend "sqlite" }} + volumeMounts: + - name: metadata + mountPath: /var/lib/gohoarder/metadata + {{- end }} + {{- end }} containers: - name: server securityContext: @@ -125,6 +196,29 @@ spec: name: {{ include "gohoarder.fullname" . }}-postgresql key: password {{- end }} + {{- if and (or (eq .Values.metadata.backend "mysql") (eq .Values.metadata.backend "mariadb")) .Values.metadata.mysql.existingSecret }} + - name: MYSQL_USER + valueFrom: + secretKeyRef: + name: {{ .Values.metadata.mysql.existingSecret }} + key: username + - name: MYSQL_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.metadata.mysql.existingSecret }} + key: password + {{- else if and (or (eq .Values.metadata.backend "mysql") (eq .Values.metadata.backend "mariadb")) .Values.metadata.mysql.username }} + - name: MYSQL_USER + valueFrom: + secretKeyRef: + name: {{ include "gohoarder.fullname" . }}-mysql + key: username + - name: MYSQL_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "gohoarder.fullname" . }}-mysql + key: password + {{- end }} {{- if and .Values.security.scanners.ghsa.enabled .Values.security.scanners.ghsa.existingSecret }} - name: GHSA_TOKEN valueFrom: diff --git a/charts/gohoarder/templates/secret.yaml b/charts/gohoarder/templates/secret.yaml index cfa1876..656ffd9 100644 --- a/charts/gohoarder/templates/secret.yaml +++ b/charts/gohoarder/templates/secret.yaml @@ -53,6 +53,19 @@ data: password: {{ .Values.metadata.postgresql.password | b64enc | quote }} {{- end }} --- +{{- if and (or (eq .Values.metadata.backend "mysql") (eq .Values.metadata.backend "mariadb")) (not .Values.metadata.mysql.existingSecret) .Values.metadata.mysql.username }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "gohoarder.fullname" . }}-mysql + labels: + {{- include "gohoarder.labels" . | nindent 4 }} +type: Opaque +data: + username: {{ .Values.metadata.mysql.username | b64enc | quote }} + password: {{ .Values.metadata.mysql.password | b64enc | quote }} +{{- end }} +--- {{- if and .Values.security.scanners.ghsa.enabled (not .Values.security.scanners.ghsa.existingSecret) .Values.security.scanners.ghsa.token }} apiVersion: v1 kind: Secret diff --git a/charts/gohoarder/values.yaml b/charts/gohoarder/values.yaml index c3431ba..39a69d5 100644 --- a/charts/gohoarder/values.yaml +++ b/charts/gohoarder/values.yaml @@ -51,17 +51,17 @@ image: server: repository: ghcr.io/lukaszraczylo/gohoarder-server pullPolicy: IfNotPresent - tag: "0.1.10" + tag: "0.1.13" frontend: repository: ghcr.io/lukaszraczylo/gohoarder-frontend pullPolicy: IfNotPresent - tag: "0.1.10" + tag: "0.1.13" scanner: repository: ghcr.io/lukaszraczylo/gohoarder-scanner pullPolicy: IfNotPresent - tag: "0.1.10" + tag: "0.1.13" # Service Account serviceAccount: @@ -272,7 +272,7 @@ storage: # Metadata storage configuration metadata: - # Backend: sqlite, postgresql + # Backend: sqlite, postgresql, mysql # # IMPORTANT: SQLite CANNOT be used with SMB/CIFS/NFS network storage! # SQLite requires POSIX file locking which causes "database is locked" errors on network filesystems. @@ -286,6 +286,13 @@ metadata: # 2. PostgreSQL with any storage (RECOMMENDED for production) # - Set backend: postgresql # - Configure postgresql settings below + # - Works with any storage including SMB/NFS + # - Supports multiple replicas and high availability + # + # 3. MySQL/MariaDB with any storage (alternative to PostgreSQL) + # - Set backend: mysql + # - Configure mysql settings below + # - Works with any storage including SMB/NFS # backend: "sqlite" @@ -305,6 +312,8 @@ metadata: walMode: false # PostgreSQL configuration + # Works with any storage including SMB/NFS + # Recommended for production deployments postgresql: # Use bundled PostgreSQL (sets up postgresql subchart) enabled: false @@ -313,10 +322,57 @@ metadata: database: "gohoarder" username: "gohoarder" password: "" - sslMode: "disable" + sslMode: "disable" # disable, require, verify-ca, verify-full # Use existing secret for PostgreSQL credentials existingSecret: "" + # MySQL/MariaDB configuration + # Works with any storage including SMB/NFS + # Alternative to PostgreSQL for production deployments + mysql: + host: "localhost" + port: 3306 + database: "gohoarder" + username: "gohoarder" + password: "" + charset: "utf8mb4" + parseTime: true + # Use existing secret for MySQL credentials + existingSecret: "" + + # GORM connection pool settings (applies to all database backends) + # These settings control database connection pooling and performance + maxOpenConns: 25 # Maximum number of open connections to the database + maxIdleConns: 5 # Maximum number of idle connections in the pool + connMaxLifetime: 3600 # Maximum lifetime of a connection in seconds (1 hour) + logLevel: "warn" # GORM log level: silent, error, warn, info + +# Database migration configuration +migration: + # Enable automatic database migrations via init containers + # When enabled, each pod will run migrations before starting the main container + # Gormigrate handles concurrency automatically - safe for multiple pods + enabled: true + + # Migration image configuration + image: + repository: ghcr.io/lukaszraczylo/gohoarder-migrate + pullPolicy: IfNotPresent + tag: "0.1.13" + + # Migration settings + logLevel: "info" # debug, info, warn, error + timeout: "5m" # Maximum time for migrations to complete + + # Resource limits for migration init container + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 100m + memory: 128Mi + # Cache configuration cache: defaultTTL: "168h" # 7 days diff --git a/charts/packages/gohoarder-0.1.13.tgz b/charts/packages/gohoarder-0.1.13.tgz new file mode 100644 index 0000000..df8752c Binary files /dev/null and b/charts/packages/gohoarder-0.1.13.tgz differ diff --git a/index.yaml b/index.yaml index 4fc187a..6bc2e5c 100644 --- a/index.yaml +++ b/index.yaml @@ -1,6 +1,33 @@ apiVersion: v1 entries: gohoarder: + - apiVersion: v2 + appVersion: 0.1.13 + created: "2026-01-03T21:34:20.568127742Z" + description: A universal package cache proxy supporting npm, PyPI, and Go modules + with security scanning + digest: 2dea0bf5eca95f1c9f0505fc702df15b5a7c28e28509d5837f15320ddb07d773 + home: https://github.com/lukaszraczylo/gohoarder + icon: https://raw.githubusercontent.com/lukaszraczylo/gohoarder/main/docs/logo.png + keywords: + - package-manager + - cache + - proxy + - npm + - pypi + - go-modules + - security + - vulnerability-scanning + maintainers: + - email: lukasz@raczylo.com + name: Lukasz Raczylo + name: gohoarder + sources: + - https://github.com/lukaszraczylo/gohoarder + type: application + urls: + - https://github.com/lukaszraczylo/helm-charts/releases/download/gohoarder-0.1.13/gohoarder-0.1.13.tgz + version: 0.1.13 - apiVersion: v2 appVersion: 0.1.10 created: "2026-01-03T12:36:54.68991809Z" @@ -1786,4 +1813,4 @@ entries: urls: - https://github.com/lukaszraczylo/helm-charts/releases/download/kubemirror-0.2.8/kubemirror-0.2.8.tgz version: 0.2.8 -generated: "2026-01-03T12:36:54.688487556Z" +generated: "2026-01-03T21:34:20.566521227Z"