From 162d4241cdec4f05e65d5b759ddafb485786815f Mon Sep 17 00:00:00 2001 From: Masaki Yatsu Date: Sun, 30 Nov 2025 16:22:19 +0900 Subject: [PATCH] feat(falkordb): install FalkorDB --- charts/falkordb/.helmignore | 23 ++ charts/falkordb/Chart.yaml | 15 ++ charts/falkordb/templates/NOTES.txt | 33 +++ charts/falkordb/templates/_helpers.tpl | 62 +++++ charts/falkordb/templates/service.yaml | 38 +++ charts/falkordb/templates/serviceaccount.yaml | 13 + charts/falkordb/templates/servicemonitor.yaml | 18 ++ charts/falkordb/templates/statefulset.yaml | 135 ++++++++++ charts/falkordb/values.yaml | 90 +++++++ falkordb/.gitignore | 2 + falkordb/README.md | 255 ++++++++++++++++++ ...rdb-password-external-secret.gomplate.yaml | 18 ++ falkordb/falkordb-values.gomplate.yaml | 19 ++ falkordb/justfile | 193 +++++++++++++ justfile | 1 + 15 files changed, 915 insertions(+) create mode 100644 charts/falkordb/.helmignore create mode 100644 charts/falkordb/Chart.yaml create mode 100644 charts/falkordb/templates/NOTES.txt create mode 100644 charts/falkordb/templates/_helpers.tpl create mode 100644 charts/falkordb/templates/service.yaml create mode 100644 charts/falkordb/templates/serviceaccount.yaml create mode 100644 charts/falkordb/templates/servicemonitor.yaml create mode 100644 charts/falkordb/templates/statefulset.yaml create mode 100644 charts/falkordb/values.yaml create mode 100644 falkordb/.gitignore create mode 100644 falkordb/README.md create mode 100644 falkordb/falkordb-password-external-secret.gomplate.yaml create mode 100644 falkordb/falkordb-values.gomplate.yaml create mode 100644 falkordb/justfile diff --git a/charts/falkordb/.helmignore b/charts/falkordb/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/charts/falkordb/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/falkordb/Chart.yaml b/charts/falkordb/Chart.yaml new file mode 100644 index 0000000..bea3bc2 --- /dev/null +++ b/charts/falkordb/Chart.yaml @@ -0,0 +1,15 @@ +apiVersion: v2 +name: falkordb +description: FalkorDB - A graph database with vector similarity search +type: application +version: 0.1.0 +appVersion: "v4.14.8" +keywords: + - falkordb + - graph + - database + - redis + - cypher +home: https://www.falkordb.com/ +sources: + - https://github.com/FalkorDB/FalkorDB diff --git a/charts/falkordb/templates/NOTES.txt b/charts/falkordb/templates/NOTES.txt new file mode 100644 index 0000000..afa834b --- /dev/null +++ b/charts/falkordb/templates/NOTES.txt @@ -0,0 +1,33 @@ +FalkorDB has been deployed! + +Get the connection information: + + Host: {{ include "falkordb.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local + Port: {{ .Values.service.port }} + +{{- if .Values.auth.enabled }} + +Get the password: + + kubectl get secret {{ .Values.auth.existingSecret | default (printf "%s-password" (include "falkordb.fullname" .)) }} \ + -n {{ .Release.Namespace }} \ + -o jsonpath="{.data.{{ .Values.auth.existingSecretPasswordKey }}}" | base64 -d +{{- end }} + +Test the connection: + + redis-cli -h {{ include "falkordb.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local \ + -p {{ .Values.service.port }} \ + {{- if .Values.auth.enabled }} + -a \ + {{- end }} + PING + +Run a Cypher query: + + redis-cli -h {{ include "falkordb.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local \ + -p {{ .Values.service.port }} \ + {{- if .Values.auth.enabled }} + -a \ + {{- end }} + GRAPH.QUERY mygraph "CREATE (:Person {name: 'Alice'})" diff --git a/charts/falkordb/templates/_helpers.tpl b/charts/falkordb/templates/_helpers.tpl new file mode 100644 index 0000000..1e1f18f --- /dev/null +++ b/charts/falkordb/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "falkordb.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "falkordb.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "falkordb.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "falkordb.labels" -}} +helm.sh/chart: {{ include "falkordb.chart" . }} +{{ include "falkordb.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "falkordb.selectorLabels" -}} +app.kubernetes.io/name: {{ include "falkordb.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "falkordb.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "falkordb.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/charts/falkordb/templates/service.yaml b/charts/falkordb/templates/service.yaml new file mode 100644 index 0000000..926cbcf --- /dev/null +++ b/charts/falkordb/templates/service.yaml @@ -0,0 +1,38 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "falkordb.fullname" . }} + labels: + {{- include "falkordb.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: redis + protocol: TCP + name: redis + {{- if .Values.metrics.enabled }} + - port: 9121 + targetPort: metrics + protocol: TCP + name: metrics + {{- end }} + selector: + {{- include "falkordb.selectorLabels" . | nindent 4 }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "falkordb.fullname" . }}-headless + labels: + {{- include "falkordb.labels" . | nindent 4 }} +spec: + type: ClusterIP + clusterIP: None + ports: + - port: {{ .Values.service.port }} + targetPort: redis + protocol: TCP + name: redis + selector: + {{- include "falkordb.selectorLabels" . | nindent 4 }} diff --git a/charts/falkordb/templates/serviceaccount.yaml b/charts/falkordb/templates/serviceaccount.yaml new file mode 100644 index 0000000..4fa8f64 --- /dev/null +++ b/charts/falkordb/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "falkordb.serviceAccountName" . }} + labels: + {{- include "falkordb.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/charts/falkordb/templates/servicemonitor.yaml b/charts/falkordb/templates/servicemonitor.yaml new file mode 100644 index 0000000..418dc68 --- /dev/null +++ b/charts/falkordb/templates/servicemonitor.yaml @@ -0,0 +1,18 @@ +{{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "falkordb.fullname" . }} + labels: + {{- include "falkordb.labels" . | nindent 4 }} + {{- with .Values.metrics.serviceMonitor.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + {{- include "falkordb.selectorLabels" . | nindent 6 }} + endpoints: + - port: metrics + interval: {{ .Values.metrics.serviceMonitor.interval }} +{{- end }} diff --git a/charts/falkordb/templates/statefulset.yaml b/charts/falkordb/templates/statefulset.yaml new file mode 100644 index 0000000..34770c5 --- /dev/null +++ b/charts/falkordb/templates/statefulset.yaml @@ -0,0 +1,135 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "falkordb.fullname" . }} + labels: + {{- include "falkordb.labels" . | nindent 4 }} +spec: + serviceName: {{ include "falkordb.fullname" . }}-headless + replicas: 1 + selector: + matchLabels: + {{- include "falkordb.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "falkordb.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "falkordb.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: + - /bin/sh + - -c + - | + redis-server \ + --loadmodule /var/lib/falkordb/bin/falkordb.so \ + {{- if .Values.auth.enabled }} + --requirepass "$REDIS_PASSWORD" + {{- end }} + env: + {{- if .Values.auth.enabled }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.auth.existingSecret | default (printf "%s-password" (include "falkordb.fullname" .)) }} + key: {{ .Values.auth.existingSecretPasswordKey }} + - name: REDISCLI_AUTH + valueFrom: + secretKeyRef: + name: {{ .Values.auth.existingSecret | default (printf "%s-password" (include "falkordb.fullname" .)) }} + key: {{ .Values.auth.existingSecretPasswordKey }} + {{- end }} + ports: + - name: redis + containerPort: 6379 + protocol: TCP + livenessProbe: + {{- toYaml .Values.livenessProbe | nindent 12 }} + readinessProbe: + {{- toYaml .Values.readinessProbe | nindent 12 }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + - name: data + mountPath: /var/lib/falkordb/data + {{- if .Values.metrics.enabled }} + - name: metrics + image: "{{ .Values.metrics.image.repository }}:{{ .Values.metrics.image.tag }}" + imagePullPolicy: {{ .Values.metrics.image.pullPolicy }} + securityContext: + runAsUser: 1001 + runAsGroup: 1001 + runAsNonRoot: true + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + seccompProfile: + type: RuntimeDefault + capabilities: + drop: + - ALL + env: + {{- if .Values.auth.enabled }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.auth.existingSecret | default (printf "%s-password" (include "falkordb.fullname" .)) }} + key: {{ .Values.auth.existingSecretPasswordKey }} + {{- end }} + args: + - "--redis.addr=redis://localhost:6379" + {{- if .Values.auth.enabled }} + - "--redis.password=$(REDIS_PASSWORD)" + {{- end }} + ports: + - name: metrics + containerPort: 9121 + protocol: TCP + resources: + {{- toYaml .Values.metrics.resources | nindent 12 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.persistence.enabled }} + volumeClaimTemplates: + - metadata: + name: data + labels: + {{- include "falkordb.labels" . | nindent 10 }} + spec: + accessModes: + {{- toYaml .Values.persistence.accessModes | nindent 10 }} + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size }} + {{- end }} diff --git a/charts/falkordb/values.yaml b/charts/falkordb/values.yaml new file mode 100644 index 0000000..ee18774 --- /dev/null +++ b/charts/falkordb/values.yaml @@ -0,0 +1,90 @@ +image: + repository: falkordb/falkordb + pullPolicy: IfNotPresent + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +auth: + enabled: true + existingSecret: "" + existingSecretPasswordKey: "redis-password" + password: "" + +persistence: + enabled: true + storageClass: "" + accessModes: + - ReadWriteOnce + size: 8Gi + +serviceAccount: + create: true + automount: false + annotations: {} + name: "" + +podAnnotations: {} +podLabels: {} + +podSecurityContext: {} + +securityContext: {} + +service: + type: ClusterIP + port: 6379 + +resources: + requests: + cpu: 25m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi + +livenessProbe: + exec: + command: + - redis-cli + - ping + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + +readinessProbe: + exec: + command: + - redis-cli + - ping + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +metrics: + enabled: false + image: + repository: oliver006/redis_exporter + tag: v1.66.0 + pullPolicy: IfNotPresent + resources: + requests: + cpu: 15m + memory: 64Mi + limits: + cpu: 100m + memory: 128Mi + serviceMonitor: + enabled: false + interval: 30s + labels: {} diff --git a/falkordb/.gitignore b/falkordb/.gitignore new file mode 100644 index 0000000..9576c20 --- /dev/null +++ b/falkordb/.gitignore @@ -0,0 +1,2 @@ +falkordb-password-external-secret.yaml +falkordb-values.yaml diff --git a/falkordb/README.md b/falkordb/README.md new file mode 100644 index 0000000..bd3214f --- /dev/null +++ b/falkordb/README.md @@ -0,0 +1,255 @@ +# FalkorDB + +FalkorDB is a high-performance graph database with vector similarity search capabilities, designed for knowledge graphs and GraphRAG applications. It uses OpenCypher as its query language and is Redis-compatible. + +## Table of Contents + +- [Installation](#installation) +- [Prerequisites](#prerequisites) +- [Connection Information](#connection-information) +- [Usage](#usage) +- [Configuration](#configuration) +- [Cognee Integration](#cognee-integration) +- [Management](#management) +- [Troubleshooting](#troubleshooting) + +## Installation + +Install FalkorDB with interactive configuration: + +```bash +just falkordb::install +``` + +This will: + +- Create the `falkordb` namespace with Pod Security Standards (baseline) +- Generate and store a password in Vault (or Kubernetes Secret) +- Deploy FalkorDB using the custom Helm chart +- Optionally enable Prometheus monitoring + +## Prerequisites + +- Kubernetes cluster with Longhorn storage +- For secret management: Vault and External Secrets Operator (optional but recommended) +- For monitoring: kube-prometheus-stack (optional) + +## Connection Information + +| Property | Value | +| -------- | ------------------------------------- | +| Host | `falkordb.falkordb.svc.cluster.local` | +| Port | `6379` | +| Protocol | Redis (Bolt not supported) | + +## Usage + +### Get Password + +```bash +just falkordb::get-password +``` + +### Health Check + +Requires [telepresence](https://www.telepresence.io/) connection: + +```bash +telepresence connect +just falkordb::health-check +``` + +### Test Graph Operations + +Run a basic test that creates nodes, relationships, and queries: + +```bash +just falkordb::test +``` + +### Redis CLI + +Connect directly using redis-cli: + +```bash +PASSWORD=$(just falkordb::get-password) +redis-cli -h falkordb.falkordb.svc.cluster.local -p 6379 -a "$PASSWORD" +``` + +### Cypher Queries + +FalkorDB uses OpenCypher query language via Redis commands: + +```bash +# Create a node +redis-cli -a "$PASSWORD" GRAPH.QUERY mygraph "CREATE (:Person {name: 'Alice', age: 30})" + +# Create a relationship +redis-cli -a "$PASSWORD" GRAPH.QUERY mygraph \ + "MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'}) CREATE (a)-[:KNOWS]->(b)" + +# Query the graph +redis-cli -a "$PASSWORD" GRAPH.QUERY mygraph \ + "MATCH (p:Person)-[:KNOWS]->(friend) RETURN p.name, friend.name" + +# Delete a graph +redis-cli -a "$PASSWORD" GRAPH.DELETE mygraph +``` + +### Python Client + +```python +from falkordb import FalkorDB + +db = FalkorDB( + host='falkordb.falkordb.svc.cluster.local', + port=6379, + password='' +) +graph = db.select_graph('knowledge') + +# Create nodes +graph.query("CREATE (:Concept {name: 'Machine Learning'})") +graph.query("CREATE (:Concept {name: 'Neural Networks'})") + +# Create relationship +graph.query(""" + MATCH (a:Concept {name: 'Neural Networks'}), (b:Concept {name: 'Machine Learning'}) + CREATE (a)-[:PART_OF]->(b) +""") + +# Query +result = graph.query("MATCH (c:Concept) RETURN c.name") +for record in result.result_set: + print(record) +``` + +## Configuration + +### Environment Variables + +| Variable | Default | Description | +| ---------------------- | ----------- | ----------------------- | +| `FALKORDB_NAMESPACE` | `falkordb` | Kubernetes namespace | +| `FALKORDB_VERSION` | `v4.14.8` | FalkorDB image version | +| `FALKORDB_STORAGE_SIZE`| `8Gi` | Persistent volume size | + +### Pod Security Standards + +The namespace is configured with: + +- `enforce=baseline` - Required for FalkorDB (runs as root) +- `warn=restricted` - Shows warnings for restricted violations + +## Cognee Integration + +FalkorDB can serve as both graph and vector store for [Cognee](https://github.com/topoteretes/cognee), enabling knowledge graph construction and RAG without separate vector database. + +### Configuration + +```bash +# .env for Cognee +GRAPH_DATABASE_PROVIDER=falkordb +GRAPH_DATABASE_URL=falkordb.falkordb.svc.cluster.local +GRAPH_DATABASE_PORT=6379 + +VECTOR_DB_PROVIDER=falkordb +VECTOR_DB_URL=falkordb.falkordb.svc.cluster.local +VECTOR_DB_PORT=6379 +``` + +### Usage with Cognee + +```python +import cognee + +# Add documents +await cognee.add("documents/", dataset_name="knowledge_base") + +# Generate knowledge graph (automatic) +await cognee.cognify() + +# Search with RAG +results = await cognee.search("What is the relationship between X and Y?") +``` + +## Management + +### Uninstall + +```bash +just falkordb::uninstall +``` + +### Cleanup Vault Secrets + +```bash +just falkordb::cleanup +``` + +### Available Commands + +```bash +just falkordb # List all commands +just falkordb::install # Install FalkorDB +just falkordb::uninstall # Uninstall FalkorDB +just falkordb::get-password # Get password +just falkordb::health-check # Check health (requires telepresence) +just falkordb::test # Run graph operation tests +just falkordb::cleanup # Clean up Vault secrets +``` + +## Troubleshooting + +### Pod Not Starting + +Check pod status and logs: + +```bash +kubectl get pods -n falkordb +kubectl logs falkordb-0 -n falkordb +kubectl describe pod falkordb-0 -n falkordb +``` + +### Authentication Issues + +Verify password is correctly configured: + +```bash +# Check secret exists +kubectl get secret falkordb-password -n falkordb + +# Test authentication from within cluster +kubectl exec falkordb-0 -n falkordb -- redis-cli -a "$PASSWORD" PING +``` + +### Connection Refused + +Ensure service is running: + +```bash +kubectl get svc -n falkordb +``` + +For external access, use telepresence or port-forward: + +```bash +kubectl port-forward svc/falkordb -n falkordb 6379:6379 +``` + +### Memory Issues + +FalkorDB stores graphs in memory. Monitor usage: + +```bash +kubectl top pod -n falkordb +``` + +If running out of memory, increase limits in `falkordb-values.gomplate.yaml`. + +## References + +- [FalkorDB Documentation](https://docs.falkordb.com/) +- [FalkorDB GitHub](https://github.com/FalkorDB/FalkorDB) +- [OpenCypher Query Language](https://opencypher.org/) +- [Cognee Documentation](https://docs.cognee.ai/) diff --git a/falkordb/falkordb-password-external-secret.gomplate.yaml b/falkordb/falkordb-password-external-secret.gomplate.yaml new file mode 100644 index 0000000..a88b5e4 --- /dev/null +++ b/falkordb/falkordb-password-external-secret.gomplate.yaml @@ -0,0 +1,18 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: falkordb-password-external-secret + namespace: {{ .Env.FALKORDB_NAMESPACE }} +spec: + refreshInterval: 1h + secretStoreRef: + name: vault-secret-store + kind: ClusterSecretStore + target: + name: falkordb-password + creationPolicy: Owner + data: + - secretKey: redis-password + remoteRef: + key: falkordb/auth + property: password diff --git a/falkordb/falkordb-values.gomplate.yaml b/falkordb/falkordb-values.gomplate.yaml new file mode 100644 index 0000000..ceab21d --- /dev/null +++ b/falkordb/falkordb-values.gomplate.yaml @@ -0,0 +1,19 @@ +image: + tag: {{ .Env.FALKORDB_VERSION }} + +auth: + enabled: true + existingSecret: falkordb-password + existingSecretPasswordKey: redis-password + +persistence: + enabled: true + size: {{ .Env.FALKORDB_STORAGE_SIZE }} + +resources: + requests: + cpu: 25m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi diff --git a/falkordb/justfile b/falkordb/justfile new file mode 100644 index 0000000..016c501 --- /dev/null +++ b/falkordb/justfile @@ -0,0 +1,193 @@ +set fallback := true + +export FALKORDB_NAMESPACE := env("FALKORDB_NAMESPACE", "falkordb") +export FALKORDB_VERSION := env("FALKORDB_VERSION", "v4.14.8") +export FALKORDB_STORAGE_SIZE := env("FALKORDB_STORAGE_SIZE", "8Gi") +export EXTERNAL_SECRETS_NAMESPACE := env("EXTERNAL_SECRETS_NAMESPACE", "external-secrets") +export PROMETHEUS_NAMESPACE := env("PROMETHEUS_NAMESPACE", "monitoring") +export MONITORING_ENABLED := env("MONITORING_ENABLED", "") + +[private] +default: + @just --list --unsorted --list-submodules + +# Create FalkorDB namespace +create-namespace: + @kubectl get namespace ${FALKORDB_NAMESPACE} &>/dev/null || \ + kubectl create namespace ${FALKORDB_NAMESPACE} + @kubectl label namespace ${FALKORDB_NAMESPACE} \ + pod-security.kubernetes.io/enforce=baseline \ + pod-security.kubernetes.io/warn=restricted \ + --overwrite + +# Delete FalkorDB namespace +delete-namespace: + @kubectl delete namespace ${FALKORDB_NAMESPACE} --ignore-not-found + +# Create FalkorDB password secret +create-password-secret: + #!/bin/bash + set -euo pipefail + echo "Setting up FalkorDB password..." + + PASSWORD=$(just utils::random-password) + + if helm status external-secrets -n ${EXTERNAL_SECRETS_NAMESPACE} &>/dev/null; then + echo "External Secrets available. Storing password in Vault and creating ExternalSecret..." + just vault::put falkordb/auth password="$PASSWORD" + gomplate -f falkordb-password-external-secret.gomplate.yaml -o falkordb-password-external-secret.yaml + kubectl apply -f falkordb-password-external-secret.yaml + echo "Waiting for password secret to be ready..." + kubectl wait --for=condition=Ready externalsecret/falkordb-password-external-secret \ + -n ${FALKORDB_NAMESPACE} --timeout=60s + else + echo "External Secrets not available. Creating Kubernetes Secret directly..." + kubectl delete secret falkordb-password -n ${FALKORDB_NAMESPACE} --ignore-not-found + kubectl create secret generic falkordb-password -n ${FALKORDB_NAMESPACE} \ + --from-literal=redis-password="$PASSWORD" + if helm status vault -n vault &>/dev/null; then + just vault::put falkordb/auth password="$PASSWORD" + fi + fi + echo "FalkorDB password setup completed" + +# Delete FalkorDB password secret +delete-password-secret: + @kubectl delete secret falkordb-password -n ${FALKORDB_NAMESPACE} --ignore-not-found + @kubectl delete externalsecret falkordb-password-external-secret -n ${FALKORDB_NAMESPACE} --ignore-not-found + +# Install FalkorDB +install: + #!/bin/bash + set -euo pipefail + just create-namespace + just create-password-secret + + METRICS_ENABLED="false" + SERVICEMONITOR_ENABLED="false" + + if helm status kube-prometheus-stack -n ${PROMETHEUS_NAMESPACE} &>/dev/null; then + if [ -z "${MONITORING_ENABLED}" ]; then + if gum confirm "Enable Prometheus monitoring?"; then + MONITORING_ENABLED="true" + else + MONITORING_ENABLED="false" + fi + fi + if [ "${MONITORING_ENABLED}" = "true" ]; then + METRICS_ENABLED="true" + SERVICEMONITOR_ENABLED="true" + kubectl label namespace ${FALKORDB_NAMESPACE} buun.channel/enable-monitoring=true --overwrite + fi + fi + + gomplate -f falkordb-values.gomplate.yaml -o falkordb-values.yaml + helm upgrade --install falkordb ../charts/falkordb \ + -n ${FALKORDB_NAMESPACE} --create-namespace --wait \ + -f falkordb-values.yaml \ + --set metrics.enabled=${METRICS_ENABLED} \ + --set metrics.serviceMonitor.enabled=${SERVICEMONITOR_ENABLED} \ + --set metrics.serviceMonitor.labels.release=kube-prometheus-stack + + echo "" + echo "FalkorDB installed successfully!" + echo "Connection: falkordb.${FALKORDB_NAMESPACE}.svc.cluster.local:6379" + +# Uninstall FalkorDB +uninstall: + #!/bin/bash + set -euo pipefail + if gum confirm "Are you sure you want to uninstall FalkorDB?"; then + helm uninstall falkordb -n ${FALKORDB_NAMESPACE} --wait --ignore-not-found + just delete-password-secret + just delete-namespace + echo "FalkorDB uninstalled" + else + echo "Uninstall cancelled" + fi + +# Get FalkorDB password +get-password: + @kubectl get secret falkordb-password -n ${FALKORDB_NAMESPACE} \ + -o jsonpath="{.data.redis-password}" | base64 -d + @echo + +# Get FalkorDB service URL +[private] +get-service-url: + @echo "redis://falkordb.${FALKORDB_NAMESPACE}.svc.cluster.local:6379" + +# Check if telepresence is connected +[private] +check-telepresence: + #!/bin/bash + set -euo pipefail + if ! command -v telepresence &>/dev/null; then + echo "Error: telepresence is not installed" >&2 + exit 1 + fi + if ! telepresence status &>/dev/null; then + echo "Error: telepresence is not connected" >&2 + echo "Please run: telepresence connect" >&2 + exit 1 + fi + +# Check FalkorDB health +health-check: + #!/bin/bash + set -euo pipefail + just check-telepresence + PASSWORD=$(just get-password) + HOST="falkordb.${FALKORDB_NAMESPACE}.svc.cluster.local" + echo "Checking FalkorDB health at ${HOST}:6379..." + redis-cli -h ${HOST} -p 6379 -a "${PASSWORD}" --no-auth-warning PING + +# Test FalkorDB with basic graph operations +test: + #!/bin/bash + set -euo pipefail + just check-telepresence + PASSWORD=$(just get-password) + HOST="falkordb.${FALKORDB_NAMESPACE}.svc.cluster.local" + GRAPH_NAME="test_graph_$(date +%s)" + + echo "Testing FalkorDB at ${HOST}:6379" + echo "Using graph: ${GRAPH_NAME}" + echo + + echo "1. Creating nodes..." + redis-cli -h ${HOST} -p 6379 -a "${PASSWORD}" --no-auth-warning \ + GRAPH.QUERY ${GRAPH_NAME} "CREATE (:Person {name: 'Alice', age: 30})" + redis-cli -h ${HOST} -p 6379 -a "${PASSWORD}" --no-auth-warning \ + GRAPH.QUERY ${GRAPH_NAME} "CREATE (:Person {name: 'Bob', age: 25})" + echo + + echo "2. Creating relationship..." + redis-cli -h ${HOST} -p 6379 -a "${PASSWORD}" --no-auth-warning \ + GRAPH.QUERY ${GRAPH_NAME} "MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'}) CREATE (a)-[:KNOWS {since: 2020}]->(b)" + echo + + echo "3. Querying graph..." + redis-cli -h ${HOST} -p 6379 -a "${PASSWORD}" --no-auth-warning \ + GRAPH.QUERY ${GRAPH_NAME} "MATCH (p:Person)-[:KNOWS]->(friend) RETURN p.name, friend.name" + echo + + echo "4. Deleting test graph..." + redis-cli -h ${HOST} -p 6379 -a "${PASSWORD}" --no-auth-warning \ + GRAPH.DELETE ${GRAPH_NAME} + echo + + echo "Test completed successfully!" + +# Clean up FalkorDB resources +cleanup: + #!/bin/bash + set -euo pipefail + echo "This will delete all FalkorDB resources and secrets." + if gum confirm "Are you sure you want to proceed?"; then + echo "Cleaning up FalkorDB resources..." + just vault::delete falkordb/auth || true + echo "Cleanup completed" + else + echo "Cleanup cancelled" + fi diff --git a/justfile b/justfile index aacb34e..3ffc0ed 100644 --- a/justfile +++ b/justfile @@ -14,6 +14,7 @@ mod dagster mod datahub mod env mod external-secrets +mod falkordb mod goldilocks mod keycloak mod jupyterhub