diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..0051c2c
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,88 @@
+name: CI
+
+on:
+ pull_request:
+ branches: [main]
+ push:
+ branches: [main]
+ workflow_call:
+
+jobs:
+ lint:
+ name: Lint
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Install Helm
+ uses: azure/setup-helm@v4
+ with:
+ version: v3.16.0
+
+ - name: Lint chart
+ run: helm lint .
+
+ - name: Validate JSON schema
+ run: |
+ if [ -f values.schema.json ]; then
+ python3 -m json.tool values.schema.json > /dev/null
+ echo "✅ values.schema.json is valid"
+ fi
+
+ test:
+ name: Test
+ runs-on: ubuntu-latest
+ needs: lint
+ strategy:
+ fail-fast: false
+ matrix:
+ config:
+ - name: standalone-default
+ args: ""
+ - name: standalone-auth
+ args: "--set auth.enabled=true --set auth.password=test"
+ - name: standalone-metrics
+ args: "--set metrics.enabled=true"
+ - name: sentinel-mode
+ args: "--set architecture=sentinel --set auth.enabled=true --set auth.password=test"
+ - name: sentinel-metrics
+ args: "--set architecture=sentinel --set metrics.enabled=true"
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Install Helm
+ uses: azure/setup-helm@v4
+ with:
+ version: v3.16.0
+
+ - name: Test template - ${{ matrix.config.name }}
+ run: |
+ helm template test . ${{ matrix.config.args }} > output.yaml
+
+ # Validate required resources
+ grep -q "kind: StatefulSet" output.yaml
+ grep -q "kind: Service" output.yaml
+ grep -q "kind: ConfigMap" output.yaml
+
+ echo "✅ ${{ matrix.config.name }} passed"
+
+ package:
+ name: Package
+ runs-on: ubuntu-latest
+ needs: test
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Install Helm
+ uses: azure/setup-helm@v4
+ with:
+ version: v3.16.0
+
+ - name: Package chart
+ run: |
+ helm package .
+ ls -la *.tgz
+ echo "✅ Package successful"
diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml
deleted file mode 100644
index 6282d40..0000000
--- a/.github/workflows/pages.yml
+++ /dev/null
@@ -1,234 +0,0 @@
-name: Deploy GitHub Pages
-
-on:
- push:
- branches:
- - main
- paths:
- - 'Chart.yaml'
- - 'README.md'
- - 'artifacthub-repo.yml'
- - '.github/workflows/pages.yml'
- workflow_dispatch:
-
-# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
-permissions:
- contents: read
- pages: write
- id-token: write
-
-# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
-# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
-concurrency:
- group: "pages"
- cancel-in-progress: false
-
-jobs:
- # Build job
- build:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- fetch-depth: 0
-
- - name: Setup Pages
- uses: actions/configure-pages@v4
- continue-on-error: true
-
- - name: Install Helm
- uses: azure/setup-helm@v4
- with:
- version: v3.12.1
-
- - name: Create Helm repository index
- run: |
- # Create directories
- mkdir -p .cr-release-packages
- mkdir -p public
-
- # Download existing index.yaml and .tgz files to preserve history
- echo "Downloading existing repository files..."
- wget -q https://start-codex.github.io/valkey-helm-chart/index.yaml -O existing-index.yaml || echo "No existing index.yaml found"
-
- # Download existing .tgz files if index exists
- if [ -f existing-index.yaml ]; then
- echo "Parsing existing chart versions..."
- # Extract URLs of existing chart packages from index.yaml and download to both directories
- urls=$(grep -o 'https://start-codex.github.io/valkey-helm-chart/[^"]*\.tgz' existing-index.yaml || true)
- if [ -n "$urls" ]; then
- echo "$urls" | while read url; do
- if [ -n "$url" ]; then
- filename=$(basename "$url")
- echo "Downloading existing chart: $filename"
- # Download to both .cr-release-packages and public for efficiency
- wget -q "$url" -O ".cr-release-packages/$filename" || echo "Failed to download $filename to .cr-release-packages"
- wget -q "$url" -O "public/$filename" || echo "Failed to download $filename to public"
- fi
- done
- else
- echo "No existing chart packages found in index"
- fi
-
- # Copy existing index to merge later
- cp existing-index.yaml .cr-release-packages/ || true
- fi
-
- # Package the current chart
- helm package . -d .cr-release-packages
-
- # Create/update the index with existing and new charts
- if [ -f .cr-release-packages/existing-index.yaml ]; then
- echo "Merging with existing index..."
- helm repo index .cr-release-packages --url https://start-codex.github.io/valkey-helm-chart --merge .cr-release-packages/existing-index.yaml
- else
- echo "Creating new index..."
- helm repo index .cr-release-packages --url https://start-codex.github.io/valkey-helm-chart
- fi
-
- # Copy new chart files to public directory
- if ls .cr-release-packages/*.tgz 1> /dev/null 2>&1; then
- cp .cr-release-packages/*.tgz public/ || true
- fi
-
- # Copy the updated index
- if [ -f .cr-release-packages/index.yaml ]; then
- cp .cr-release-packages/index.yaml public/
- fi
-
- cp README.md public/ || true
- cp artifacthub-repo.yml public/ || true
-
- # Verify what we have
- echo "Contents of public directory:"
- ls -la public/
-
- # Show chart versions in the index
- if [ -f public/index.yaml ]; then
- echo ""
- echo "Chart versions in repository:"
- grep -A 2 "version:" public/index.yaml | grep "version:" | sort -V || echo "No versions found"
-
- echo ""
- echo "Total charts in repository:"
- grep "\.tgz" public/index.yaml | wc -l || echo "0"
- fi
-
- # Create a simple index.html
- cat > public/index.html << 'EOF'
-
-
-
- Valkey Helm Chart Repository
-
-
-
-
-
-
-
-
-
🚀 Quick Start
-
Add this repository to your Helm:
-
helm repo add valkey https://start-codex.github.io/valkey-helm-chart
helm repo update
-
-
Install Valkey:
-
helm install my-valkey valkey/valkey
-
-
-
-
📦 Available Charts
-
- - valkey - Valkey server with standalone and sentinel modes
-
-
-
-
-
🔧 Configuration
-
The chart supports both standalone and sentinel architectures:
-
# Standalone mode
helm install valkey valkey/valkey
# Sentinel mode
helm install valkey valkey/valkey --set architecture=sentinel
-
-
-
-
📚 Documentation
-
For detailed configuration options and examples, visit:
-
-
-
-
-
-
- EOF
-
- - name: Upload artifact
- uses: actions/upload-pages-artifact@v3
- with:
- path: public
-
- # Deployment job
- deploy:
- environment:
- name: github-pages
- url: ${{ steps.deployment.outputs.page_url }}
- runs-on: ubuntu-latest
- needs: build
- if: github.repository_owner != ''
- steps:
- - name: Deploy to GitHub Pages
- id: deployment
- uses: actions/deploy-pages@v4
- continue-on-error: true
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index b52ee1a..df414a1 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,47 +1,205 @@
-name: Release Charts
+name: Release
on:
+ push:
+ branches: [main]
+ paths:
+ - 'Chart.yaml'
+ - 'values.yaml'
+ - 'values.schema.json'
+ - 'templates/**'
+ - 'public-key.asc'
+ - 'artifacthub-repo.yml'
workflow_dispatch:
+permissions:
+ contents: write
+ pages: write
+ id-token: write
+
+concurrency:
+ group: release
+ cancel-in-progress: false
+
jobs:
+ ci:
+ name: CI
+ uses: ./.github/workflows/ci.yml
+
release:
- permissions:
- contents: write
+ name: Release & Deploy
runs-on: ubuntu-latest
+ needs: ci
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- - name: Configure Git
- run: |
- git config user.name "$GITHUB_ACTOR"
- git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
+ - name: Setup Pages
+ uses: actions/configure-pages@v4
- name: Install Helm
uses: azure/setup-helm@v4
with:
- version: v3.12.1
+ version: v3.16.0
- - name: Add dependency chart repos
+ - name: Import GPG key
+ env:
+ GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
run: |
- helm repo add bitnami https://charts.bitnami.com/bitnami
+ if [ -n "$GPG_PRIVATE_KEY" ]; then
+ echo "$GPG_PRIVATE_KEY" | gpg --batch --import
+ echo "allow-loopback-pinentry" >> ~/.gnupg/gpg-agent.conf
+ gpg-connect-agent reloadagent /bye || true
+ echo "GPG_AVAILABLE=true" >> $GITHUB_ENV
+ else
+ echo "GPG_AVAILABLE=false" >> $GITHUB_ENV
+ fi
- - name: Run chart-releaser
- uses: helm/chart-releaser-action@v1.6.0
- with:
- charts_dir: .
- skip_existing: true
+ - name: Package Chart
env:
- CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
- CR_SKIP_EXISTING: true
+ GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
+ run: |
+ mkdir -p .packages public
+
+ # Download existing charts
+ wget -q https://start-codex.github.io/valkey-helm-chart/index.yaml -O existing-index.yaml 2>/dev/null || true
+
+ if [ -f existing-index.yaml ]; then
+ grep -o 'https://start-codex.github.io/valkey-helm-chart/[^"]*\.tgz' existing-index.yaml | while read url; do
+ [ -n "$url" ] && wget -q "$url" -O "public/$(basename $url)" || true
+ done
+ cp existing-index.yaml .packages/
+ fi
+
+ # Package with signature if GPG available
+ if [ "$GPG_AVAILABLE" = "true" ] && [ -n "$GPG_PASSPHRASE" ]; then
+ echo "📦 Packaging with GPG signature..."
+ echo "$GPG_PASSPHRASE" | helm package . -d .packages \
+ --sign --key "cloud@startcodex.com" --passphrase-file /dev/stdin \
+ || helm package . -d .packages
+ else
+ echo "📦 Packaging without signature..."
+ helm package . -d .packages
+ fi
+
+ # Generate index
+ if [ -f .packages/existing-index.yaml ]; then
+ helm repo index .packages --url https://start-codex.github.io/valkey-helm-chart --merge .packages/existing-index.yaml
+ else
+ helm repo index .packages --url https://start-codex.github.io/valkey-helm-chart
+ fi
- - name: Trigger Pages Workflow
- if: github.ref == 'refs/heads/main'
+ # Copy files to public
+ cp .packages/*.tgz public/
+ cp .packages/*.prov public/ 2>/dev/null || true
+ cp .packages/index.yaml public/
+ cp README.md artifacthub-repo.yml public-key.asc public/
+
+ - name: Generate index.html
run: |
- curl -X POST \
- -H "Accept: application/vnd.github.v3+json" \
- -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
- https://api.github.com/repos/${{ github.repository }}/actions/workflows/pages.yml/dispatches \
- -d '{"ref":"main"}'
\ No newline at end of file
+ VERSION=$(grep '^version:' Chart.yaml | awk '{print $2}')
+ cat > public/index.html << 'HTMLEOF'
+
+
+
+
+
+ Valkey Helm Chart
+
+
+
+
+
+
+
Quick Start
+
helm repo add valkey https://start-codex.github.io/valkey-helm-chart
+ helm repo update
+ helm install my-valkey valkey/valkey
+
+
+
+
Installation Examples
+
# With authentication
+ helm install my-valkey valkey/valkey \
+ --set auth.enabled=true \
+ --set auth.password="your-password"
+
+ # High Availability (Sentinel)
+ helm install my-valkey valkey/valkey \
+ --set architecture=sentinel
+
+
+
+
Verify Signature
+
wget https://start-codex.github.io/valkey-helm-chart/public-key.asc
+ gpg --import public-key.asc
+ helm pull valkey/valkey --verify
+
+
+
+
+
+
+
+
+
+ HTMLEOF
+ sed -i "s/\${VERSION}/$VERSION/g" public/index.html
+
+ - name: Upload Pages artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: public
+
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
deleted file mode 100644
index 7b85b29..0000000
--- a/.github/workflows/test.yml
+++ /dev/null
@@ -1,60 +0,0 @@
-name: Test Chart
-
-on:
- workflow_dispatch:
- pull_request:
- branches: [ main ]
- push:
- branches: [ main ]
-
-jobs:
- test:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v4
-
- - name: Install Helm
- uses: azure/setup-helm@v4
- with:
- version: v3.12.1
-
- - name: Lint chart
- run: |
- helm lint .
- echo "✅ Chart lint passed"
-
- - name: Test chart templates
- run: |
- echo "Testing standalone mode..."
- helm template test . --set architecture=standalone > /dev/null
- echo "✅ Standalone template generation passed"
-
- echo "Testing sentinel mode..."
- helm template test . --set architecture=sentinel --set auth.enabled=true --set auth.password=test > /dev/null
- echo "✅ Sentinel template generation passed"
-
- - name: Package chart
- run: |
- helm package .
- ls -la *.tgz
- echo "✅ Chart packaging passed"
-
- - name: Test installation (template only)
- run: |
- echo "Testing template generation with default values..."
- helm template test-valkey . > /dev/null
- echo "✅ Template generation with default values passed"
-
- echo "Testing template generation with custom values..."
- cat > test-values.yaml << 'EOF'
- architecture: standalone
- auth:
- enabled: true
- password: "testpassword"
- standalone:
- persistence:
- storageClass: "standard"
- EOF
- helm template test-valkey . -f test-values.yaml > /dev/null
- echo "✅ Template generation with custom values passed"
\ No newline at end of file
diff --git a/.github/workflows/validate-configs.yml b/.github/workflows/validate-configs.yml
deleted file mode 100644
index 9398a9e..0000000
--- a/.github/workflows/validate-configs.yml
+++ /dev/null
@@ -1,109 +0,0 @@
-name: Validate Chart Configurations
-
-on:
- workflow_dispatch:
- pull_request:
- branches: [ main ]
- push:
- branches: [ main ]
-
-jobs:
- validate-configs:
- runs-on: ubuntu-latest
- strategy:
- matrix:
- config:
- - name: "standalone-no-auth"
- values: |
- architecture: standalone
- auth:
- enabled: false
- - name: "standalone-with-auth"
- values: |
- architecture: standalone
- auth:
- enabled: true
- password: "testpassword"
- - name: "standalone-existing-secret"
- values: |
- architecture: standalone
- auth:
- enabled: true
- existingSecret: "redis-secret"
- - name: "standalone-with-persistence"
- values: |
- architecture: standalone
- standalone:
- persistence:
- enabled: true
- storageClass: "longhorn-simple"
- size: "10Gi"
- - name: "sentinel-mode"
- values: |
- architecture: sentinel
- auth:
- enabled: true
- password: "testpassword"
- sentinel:
- replicaCount: 3
- - name: "with-metrics"
- values: |
- architecture: standalone
- metrics:
- enabled: true
-
- steps:
- - name: Checkout
- uses: actions/checkout@v4
-
- - name: Install Helm
- uses: azure/setup-helm@v4
- with:
- version: v3.12.1
-
- - name: Create test values file
- run: |
- cat > test-values-${{ matrix.config.name }}.yaml << 'EOF'
- ${{ matrix.config.values }}
- EOF
-
- - name: Lint chart with ${{ matrix.config.name }} config
- run: |
- helm lint . -f test-values-${{ matrix.config.name }}.yaml
- echo "✅ Lint passed for ${{ matrix.config.name }}"
-
- - name: Test template generation for ${{ matrix.config.name }}
- run: |
- helm template test-${{ matrix.config.name }} . -f test-values-${{ matrix.config.name }}.yaml > output-${{ matrix.config.name }}.yaml
- echo "✅ Template generation passed for ${{ matrix.config.name }}"
-
- - name: Validate generated manifests
- run: |
- # Check that required resources are generated
- if [ "${{ matrix.config.name }}" = "standalone-no-auth" ] || [ "${{ matrix.config.name }}" = "standalone-with-auth" ] || [ "${{ matrix.config.name }}" = "standalone-existing-secret" ] || [ "${{ matrix.config.name }}" = "standalone-with-persistence" ] || [ "${{ matrix.config.name }}" = "with-metrics" ]; then
- grep -q "kind: StatefulSet" output-${{ matrix.config.name }}.yaml || (echo "❌ StatefulSet not found" && exit 1)
- grep -q "kind: Service" output-${{ matrix.config.name }}.yaml || (echo "❌ Service not found" && exit 1)
- grep -q "kind: ConfigMap" output-${{ matrix.config.name }}.yaml || (echo "❌ ConfigMap not found" && exit 1)
- fi
-
- if [ "${{ matrix.config.name }}" = "sentinel-mode" ]; then
- grep -q "kind: StatefulSet" output-${{ matrix.config.name }}.yaml || (echo "❌ StatefulSet not found" && exit 1)
- # Should have master, replica, and sentinel services
- SERVICE_COUNT=$(grep -c "kind: Service" output-${{ matrix.config.name }}.yaml)
- if [ "$SERVICE_COUNT" -lt 3 ]; then
- echo "❌ Expected at least 3 services for sentinel mode, found $SERVICE_COUNT"
- exit 1
- fi
- fi
-
- if [[ "${{ matrix.config.name }}" == *"persistence"* ]]; then
- grep -q "volumeClaimTemplates" output-${{ matrix.config.name }}.yaml || (echo "❌ VolumeClaimTemplates not found when persistence enabled" && exit 1)
- fi
-
- echo "✅ Manifest validation passed for ${{ matrix.config.name }}"
-
- - name: Upload generated manifests
- uses: actions/upload-artifact@v4
- with:
- name: manifests-${{ matrix.config.name }}
- path: output-${{ matrix.config.name }}.yaml
\ No newline at end of file
diff --git a/Chart.yaml b/Chart.yaml
index 54cfbba..fd3669c 100644
--- a/Chart.yaml
+++ b/Chart.yaml
@@ -5,7 +5,17 @@ type: application
version: 0.1.24
appVersion: "9.0.0"
annotations:
- images: |
+ artifacthub.io/signKey: |
+ fingerprint: BD9C3A3FB5C152390B589739DA06B8958C5CE1D1
+ url: https://start-codex.github.io/valkey-helm-chart/public-key.asc
+ artifacthub.io/category: database
+ artifacthub.io/license: Apache-2.0
+ artifacthub.io/links: |
+ - name: Valkey Official
+ url: https://valkey.io/
+ - name: Chart Source
+ url: https://github.com/start-codex/valkey-helm-chart
+ artifacthub.io/images: |
- name: valkey
image: docker.io/valkey/valkey:9.0.0
- name: os-shell
@@ -14,8 +24,8 @@ annotations:
image: docker.io/oliver006/redis_exporter:v1.80.0
- name: kubectl
image: docker.io/alpine/k8s:1.31.13
- licenses: Apache-2.0
- category: database
+ artifacthub.io/containsSecurityUpdates: "false"
+ artifacthub.io/prerelease: "false"
keywords:
- valkey
- redis
diff --git a/public-key.asc b/public-key.asc
new file mode 100644
index 0000000..6ac4192
--- /dev/null
+++ b/public-key.asc
@@ -0,0 +1,52 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+9WC8JBHDuwy4luXYC9J80Vdmgn7k+zc8yeKO
+=3bnc
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/values.schema.json b/values.schema.json
new file mode 100644
index 0000000..6c5b159
--- /dev/null
+++ b/values.schema.json
@@ -0,0 +1,799 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Valkey Helm Chart Values",
+ "type": "object",
+ "properties": {
+ "architecture": {
+ "type": "string",
+ "description": "Deployment architecture: standalone or sentinel",
+ "enum": ["standalone", "sentinel"],
+ "default": "standalone"
+ },
+ "global": {
+ "type": "object",
+ "description": "Global parameters",
+ "properties": {
+ "imageRegistry": {
+ "type": "string",
+ "description": "Global Docker image registry"
+ },
+ "imagePullSecrets": {
+ "type": "array",
+ "description": "Global Docker registry secret names",
+ "items": {
+ "type": "string"
+ }
+ },
+ "storageClass": {
+ "type": "string",
+ "description": "Global StorageClass for Persistent Volume(s)"
+ }
+ }
+ },
+ "clusterDomain": {
+ "type": "string",
+ "description": "Kubernetes cluster domain",
+ "default": "cluster.local"
+ },
+ "image": {
+ "type": "object",
+ "description": "Valkey image configuration",
+ "properties": {
+ "registry": {
+ "type": "string",
+ "description": "Image registry",
+ "default": "docker.io"
+ },
+ "repository": {
+ "type": "string",
+ "description": "Image repository",
+ "default": "valkey/valkey"
+ },
+ "tag": {
+ "type": "string",
+ "description": "Image tag",
+ "default": "9.0.0"
+ },
+ "pullPolicy": {
+ "type": "string",
+ "description": "Image pull policy",
+ "enum": ["Always", "IfNotPresent", "Never"],
+ "default": "IfNotPresent"
+ }
+ }
+ },
+ "nameOverride": {
+ "type": "string",
+ "description": "String to partially override fullname template"
+ },
+ "fullnameOverride": {
+ "type": "string",
+ "description": "String to fully override fullname template"
+ },
+ "auth": {
+ "type": "object",
+ "description": "Authentication configuration",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Enable password authentication",
+ "default": false
+ },
+ "password": {
+ "type": "string",
+ "description": "Valkey password (not recommended for production)"
+ },
+ "existingSecret": {
+ "type": "string",
+ "description": "Name of existing secret containing the password"
+ },
+ "existingSecretPasswordKey": {
+ "type": "string",
+ "description": "Key in existing secret containing the password",
+ "default": "password"
+ },
+ "sentinel": {
+ "type": "boolean",
+ "description": "Enable authentication for Sentinel",
+ "default": false
+ }
+ }
+ },
+ "networkPolicy": {
+ "type": "object",
+ "description": "Network Policy configuration",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Enable NetworkPolicy",
+ "default": false
+ },
+ "allowExternal": {
+ "type": "boolean",
+ "description": "Allow external connections",
+ "default": true
+ },
+ "ingressNSMatchLabels": {
+ "type": "object",
+ "description": "Namespace labels for ingress rules"
+ },
+ "ingressNSPodMatchLabels": {
+ "type": "object",
+ "description": "Pod labels for ingress rules"
+ }
+ }
+ },
+ "rbac": {
+ "type": "object",
+ "description": "RBAC configuration",
+ "properties": {
+ "create": {
+ "type": "boolean",
+ "description": "Create RBAC resources",
+ "default": false
+ }
+ }
+ },
+ "serviceAccount": {
+ "type": "object",
+ "description": "Service Account configuration",
+ "properties": {
+ "create": {
+ "type": "boolean",
+ "description": "Create ServiceAccount",
+ "default": true
+ },
+ "annotations": {
+ "type": "object",
+ "description": "ServiceAccount annotations"
+ },
+ "name": {
+ "type": "string",
+ "description": "ServiceAccount name"
+ }
+ }
+ },
+ "podSecurityContext": {
+ "type": "object",
+ "description": "Pod Security Context",
+ "properties": {
+ "fsGroup": {
+ "type": "integer",
+ "description": "Group ID for the filesystem",
+ "default": 999
+ },
+ "runAsUser": {
+ "type": "integer",
+ "description": "User ID for the container",
+ "default": 999
+ },
+ "runAsGroup": {
+ "type": "integer",
+ "description": "Group ID for the container",
+ "default": 999
+ }
+ }
+ },
+ "securityContext": {
+ "type": "object",
+ "description": "Container Security Context",
+ "properties": {
+ "allowPrivilegeEscalation": {
+ "type": "boolean",
+ "description": "Allow privilege escalation",
+ "default": false
+ },
+ "capabilities": {
+ "type": "object",
+ "description": "Linux capabilities",
+ "properties": {
+ "drop": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "readOnlyRootFilesystem": {
+ "type": "boolean",
+ "description": "Read-only root filesystem",
+ "default": true
+ },
+ "runAsNonRoot": {
+ "type": "boolean",
+ "description": "Run as non-root user",
+ "default": true
+ },
+ "runAsUser": {
+ "type": "integer",
+ "description": "User ID for the container",
+ "default": 999
+ }
+ }
+ },
+ "commonAnnotations": {
+ "type": "object",
+ "description": "Annotations to add to all deployed objects"
+ },
+ "commonLabels": {
+ "type": "object",
+ "description": "Labels to add to all deployed objects"
+ },
+ "standalone": {
+ "type": "object",
+ "description": "Standalone mode configuration",
+ "properties": {
+ "replicaCount": {
+ "type": "integer",
+ "description": "Number of replicas",
+ "default": 1,
+ "minimum": 1
+ },
+ "resources": {
+ "type": "object",
+ "description": "Container resources",
+ "properties": {
+ "limits": {
+ "type": "object",
+ "properties": {
+ "memory": {
+ "type": "string"
+ },
+ "cpu": {
+ "type": "string"
+ }
+ }
+ },
+ "requests": {
+ "type": "object",
+ "properties": {
+ "memory": {
+ "type": "string"
+ },
+ "cpu": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "nodeSelector": {
+ "type": "object",
+ "description": "Node labels for pod assignment"
+ },
+ "tolerations": {
+ "type": "array",
+ "description": "Tolerations for pod assignment",
+ "items": {
+ "type": "object"
+ }
+ },
+ "affinity": {
+ "type": "object",
+ "description": "Affinity for pod assignment"
+ },
+ "persistence": {
+ "type": "object",
+ "description": "Persistence configuration",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Enable persistence",
+ "default": true
+ },
+ "storageClass": {
+ "type": "string",
+ "description": "StorageClass for PVC"
+ },
+ "accessModes": {
+ "type": "array",
+ "description": "PVC access modes",
+ "items": {
+ "type": "string",
+ "enum": ["ReadWriteOnce", "ReadOnlyMany", "ReadWriteMany"]
+ },
+ "default": ["ReadWriteOnce"]
+ },
+ "size": {
+ "type": "string",
+ "description": "PVC size",
+ "default": "8Gi"
+ },
+ "annotations": {
+ "type": "object",
+ "description": "PVC annotations"
+ }
+ }
+ },
+ "service": {
+ "type": "object",
+ "description": "Service configuration",
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "Service type",
+ "enum": ["ClusterIP", "NodePort", "LoadBalancer"],
+ "default": "ClusterIP"
+ },
+ "port": {
+ "type": "integer",
+ "description": "Service port",
+ "default": 6379
+ },
+ "targetPort": {
+ "type": "string",
+ "description": "Target port name",
+ "default": "valkey"
+ },
+ "annotations": {
+ "type": "object",
+ "description": "Service annotations"
+ }
+ }
+ },
+ "configuration": {
+ "type": "string",
+ "description": "Valkey configuration file content"
+ }
+ }
+ },
+ "sentinel": {
+ "type": "object",
+ "description": "Sentinel configuration",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Enable Sentinel",
+ "default": false
+ },
+ "replicaCount": {
+ "type": "integer",
+ "description": "Number of Sentinel replicas",
+ "default": 3,
+ "minimum": 1
+ },
+ "quorum": {
+ "type": "integer",
+ "description": "Quorum for Sentinel",
+ "default": 2,
+ "minimum": 1
+ },
+ "masterSet": {
+ "type": "string",
+ "description": "Name of the master set",
+ "default": "mymaster"
+ },
+ "downAfterMilliseconds": {
+ "type": "integer",
+ "description": "Time in ms to consider a master down",
+ "default": 30000
+ },
+ "failoverTimeout": {
+ "type": "integer",
+ "description": "Failover timeout in ms",
+ "default": 180000
+ },
+ "parallelSyncs": {
+ "type": "integer",
+ "description": "Number of replicas to sync in parallel",
+ "default": 1
+ },
+ "image": {
+ "type": "object",
+ "description": "Sentinel image configuration",
+ "properties": {
+ "registry": {
+ "type": "string"
+ },
+ "repository": {
+ "type": "string"
+ },
+ "tag": {
+ "type": "string"
+ },
+ "pullPolicy": {
+ "type": "string",
+ "enum": ["Always", "IfNotPresent", "Never"]
+ }
+ }
+ },
+ "resources": {
+ "type": "object",
+ "description": "Container resources"
+ },
+ "service": {
+ "type": "object",
+ "description": "Sentinel service configuration",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": ["ClusterIP", "NodePort", "LoadBalancer"]
+ },
+ "port": {
+ "type": "integer",
+ "default": 26379
+ }
+ }
+ }
+ }
+ },
+ "master": {
+ "type": "object",
+ "description": "Master configuration (sentinel mode)",
+ "properties": {
+ "replicaCount": {
+ "type": "integer",
+ "description": "Number of master replicas",
+ "default": 1
+ },
+ "resources": {
+ "type": "object",
+ "description": "Container resources"
+ },
+ "persistence": {
+ "type": "object",
+ "description": "Persistence configuration",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "default": true
+ },
+ "storageClass": {
+ "type": "string"
+ },
+ "accessModes": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "size": {
+ "type": "string",
+ "default": "8Gi"
+ }
+ }
+ },
+ "service": {
+ "type": "object",
+ "description": "Service configuration"
+ }
+ }
+ },
+ "replica": {
+ "type": "object",
+ "description": "Replica configuration (sentinel mode)",
+ "properties": {
+ "replicaCount": {
+ "type": "integer",
+ "description": "Number of replica instances",
+ "default": 2,
+ "minimum": 0
+ },
+ "resources": {
+ "type": "object",
+ "description": "Container resources"
+ },
+ "persistence": {
+ "type": "object",
+ "description": "Persistence configuration",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "default": true
+ },
+ "storageClass": {
+ "type": "string"
+ },
+ "accessModes": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "size": {
+ "type": "string",
+ "default": "8Gi"
+ }
+ }
+ },
+ "service": {
+ "type": "object",
+ "description": "Service configuration"
+ }
+ }
+ },
+ "metrics": {
+ "type": "object",
+ "description": "Metrics exporter configuration",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Enable Prometheus metrics exporter",
+ "default": false
+ },
+ "port": {
+ "type": "integer",
+ "description": "Metrics port",
+ "default": 9121
+ },
+ "image": {
+ "type": "object",
+ "description": "Metrics exporter image",
+ "properties": {
+ "registry": {
+ "type": "string",
+ "default": "docker.io"
+ },
+ "repository": {
+ "type": "string",
+ "default": "oliver006/redis_exporter"
+ },
+ "tag": {
+ "type": "string",
+ "default": "v1.80.0"
+ },
+ "pullPolicy": {
+ "type": "string",
+ "enum": ["Always", "IfNotPresent", "Never"]
+ }
+ }
+ },
+ "resources": {
+ "type": "object",
+ "description": "Container resources"
+ },
+ "serviceMonitor": {
+ "type": "object",
+ "description": "Prometheus ServiceMonitor configuration",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Create ServiceMonitor",
+ "default": false
+ },
+ "namespace": {
+ "type": "string",
+ "description": "ServiceMonitor namespace"
+ },
+ "interval": {
+ "type": "string",
+ "description": "Scrape interval"
+ },
+ "scrapeTimeout": {
+ "type": "string",
+ "description": "Scrape timeout"
+ }
+ }
+ },
+ "podMonitor": {
+ "type": "object",
+ "description": "Prometheus PodMonitor configuration",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Create PodMonitor",
+ "default": false
+ }
+ }
+ },
+ "prometheusRule": {
+ "type": "object",
+ "description": "Prometheus PrometheusRule configuration",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Create PrometheusRule",
+ "default": false
+ },
+ "rules": {
+ "type": "array",
+ "description": "Prometheus rules",
+ "items": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ },
+ "preUpgradeHook": {
+ "type": "object",
+ "description": "Pre-upgrade hook configuration",
+ "properties": {
+ "image": {
+ "type": "object",
+ "description": "Hook image configuration",
+ "properties": {
+ "registry": {
+ "type": "string",
+ "default": "docker.io"
+ },
+ "repository": {
+ "type": "string",
+ "default": "alpine/k8s"
+ },
+ "tag": {
+ "type": "string",
+ "default": "1.31.13"
+ },
+ "pullPolicy": {
+ "type": "string",
+ "enum": ["Always", "IfNotPresent", "Never"]
+ }
+ }
+ },
+ "resources": {
+ "type": "object",
+ "description": "Container resources"
+ }
+ }
+ },
+ "volumePermissions": {
+ "type": "object",
+ "description": "Volume permissions init container configuration",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Enable init container for volume permissions",
+ "default": false
+ },
+ "image": {
+ "type": "object",
+ "description": "Init container image",
+ "properties": {
+ "registry": {
+ "type": "string"
+ },
+ "repository": {
+ "type": "string"
+ },
+ "tag": {
+ "type": "string"
+ },
+ "pullPolicy": {
+ "type": "string"
+ }
+ }
+ },
+ "resources": {
+ "type": "object",
+ "description": "Container resources"
+ }
+ }
+ },
+ "sysctls": {
+ "type": "object",
+ "description": "Sysctls configuration",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Enable sysctls init container",
+ "default": false
+ }
+ }
+ },
+ "existingConfigmap": {
+ "type": "string",
+ "description": "Name of existing ConfigMap with Valkey configuration"
+ },
+ "extraEnvVars": {
+ "type": "array",
+ "description": "Extra environment variables",
+ "items": {
+ "type": "object"
+ }
+ },
+ "extraVolumes": {
+ "type": "array",
+ "description": "Extra volumes",
+ "items": {
+ "type": "object"
+ }
+ },
+ "extraVolumeMounts": {
+ "type": "array",
+ "description": "Extra volume mounts",
+ "items": {
+ "type": "object"
+ }
+ },
+ "tls": {
+ "type": "object",
+ "description": "TLS configuration",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Enable TLS",
+ "default": false
+ },
+ "port": {
+ "type": "integer",
+ "description": "TLS port",
+ "default": 6380
+ },
+ "existingSecret": {
+ "type": "string",
+ "description": "Name of existing secret with TLS certificates"
+ },
+ "certFilename": {
+ "type": "string",
+ "description": "Certificate filename in secret",
+ "default": "tls.crt"
+ },
+ "certKeyFilename": {
+ "type": "string",
+ "description": "Certificate key filename in secret",
+ "default": "tls.key"
+ },
+ "caCertFilename": {
+ "type": "string",
+ "description": "CA certificate filename in secret",
+ "default": "ca.crt"
+ },
+ "authClients": {
+ "type": "boolean",
+ "description": "Require client authentication",
+ "default": true
+ }
+ }
+ },
+ "extraDeploy": {
+ "type": "array",
+ "description": "Extra Kubernetes resources to deploy",
+ "items": {
+ "type": "object"
+ }
+ },
+ "serviceBinding": {
+ "type": "object",
+ "description": "Service Binding configuration",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Enable Service Binding",
+ "default": false
+ }
+ }
+ },
+ "headlessService": {
+ "type": "object",
+ "description": "Headless service configuration",
+ "properties": {
+ "annotations": {
+ "type": "object",
+ "description": "Headless service annotations"
+ },
+ "extraPorts": {
+ "type": "array",
+ "description": "Extra ports for headless service",
+ "items": {
+ "type": "object"
+ }
+ }
+ }
+ },
+ "podSecurityPolicy": {
+ "type": "object",
+ "description": "Pod Security Policy configuration",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Enable Pod Security Policy",
+ "default": false
+ }
+ }
+ },
+ "diagnosticMode": {
+ "type": "object",
+ "description": "Diagnostic mode configuration",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Enable diagnostic mode",
+ "default": false
+ }
+ }
+ }
+ }
+}