Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions api/apps/v1alpha1/qdrant/types.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions api/apps/v1alpha1/qdrant/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/apps/qdrant/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ include ../../../hack/package.mk
generate:
cozyvalues-gen -m 'qdrant' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/qdrant/types.go
../../../hack/update-crd.sh

test:
helm unittest .
8 changes: 8 additions & 0 deletions packages/apps/qdrant/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ Service deploys Qdrant as a StatefulSet with automatic cluster mode when multipl
| `external` | Enable external access from outside the cluster. | `bool` | `false` |


### TLS parameters

| Name | Description | Type | Value |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | ------ |
| `tls` | TLS configuration. When omitted, the effective TLS state follows the value of `external`. | `object` | `{}` |
| `tls.enabled` | Enable TLS. When unset, inherits the value of `external` (TLS is on when external access is enabled). Set explicitly to `true` or `false` to override. | `*bool` | `null` |


## Parameter examples and reference

### resources and resourcesPreset
Expand Down
78 changes: 78 additions & 0 deletions packages/apps/qdrant/templates/certmanager.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
{{- $clusterDomain := "cozy.local" }}
{{- if .Values._cluster }}
{{- $clusterDomain = (index .Values._cluster "cluster-domain") | default "cozy.local" }}
{{- end }}
{{- $tlsMap := default (dict) .Values.tls -}}
{{- $tlsEnabled := false -}}
{{- if hasKey $tlsMap "enabled" -}}
{{- $tlsEnabled = index $tlsMap "enabled" -}}
{{- else -}}
{{- $tlsEnabled = .Values.external | default false -}}
{{- end -}}
{{- if $tlsEnabled }}
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: {{ .Release.Name }}-selfsigned
namespace: {{ .Release.Namespace }}
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: {{ .Release.Name }}-ca
namespace: {{ .Release.Namespace }}
spec:
secretName: {{ .Release.Name }}-ca
duration: 43800h
commonName: {{ .Release.Name }}-ca
issuerRef:
name: {{ .Release.Name }}-selfsigned
isCA: true
privateKey:
rotationPolicy: Never
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: {{ .Release.Name }}-ca
namespace: {{ .Release.Namespace }}
spec:
ca:
secretName: {{ .Release.Name }}-ca
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: {{ .Release.Name }}-tls
namespace: {{ .Release.Namespace }}
spec:
secretName: {{ .Release.Name }}-tls
duration: 8760h
renewBefore: 720h
isCA: false
issuerRef:
name: {{ .Release.Name }}-ca
commonName: {{ .Release.Name }}
# server auth: required for REST (6333) and gRPC (6334) TLS endpoints.
# client auth: required for p2p mTLS — Qdrant uses ca_cert for peer
# verification when cluster.p2p.enable_tls is true (see qdrant.yaml).
usages:
- digital signature
- key encipherment
- server auth
- client auth
dnsNames:
- {{ .Release.Name }}
- {{ .Release.Name }}.{{ .Release.Namespace }}.svc
- {{ .Release.Name }}.{{ .Release.Namespace }}.svc.{{ $clusterDomain }}
- {{ .Release.Name }}-headless
- {{ .Release.Name }}-headless.{{ .Release.Namespace }}.svc
- {{ .Release.Name }}-headless.{{ .Release.Namespace }}.svc.{{ $clusterDomain }}
- "*.{{ .Release.Name }}-headless.{{ .Release.Namespace }}.svc.{{ $clusterDomain }}"
{{- if .Values.external }}
- {{ .Release.Name }}.{{ .Values._namespace.host }}
{{- end }}
{{- end }}
32 changes: 32 additions & 0 deletions packages/apps/qdrant/templates/qdrant.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
{{- $tlsMap := default (dict) .Values.tls -}}
{{- $tlsEnabled := false -}}
{{- if hasKey $tlsMap "enabled" -}}
{{- $tlsEnabled = index $tlsMap "enabled" -}}
{{- else -}}
{{- $tlsEnabled = .Values.external | default false -}}
{{- end -}}
Comment on lines +1 to +7
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

This logic to determine if TLS is enabled is also present in templates/certmanager.yaml. To improve maintainability and adhere to the DRY (Don't Repeat Yourself) principle, this logic should be extracted into a common helper template.

You can create a new file packages/apps/qdrant/templates/_helpers.tpl with the following content:

{{/*
Determines if TLS should be enabled based on .Values.
*/}}
{{- define "qdrant.tls.enabled" . -}}
{{- $tlsMap := .Values.tls | default dict -}}
{{- if hasKey $tlsMap "enabled" -}}
{{- $tlsMap.enabled -}}
{{- else -}}
{{- .Values.external | default false -}}
{{- end -}}
{{- end -}}

Then, you can replace this block (and the similar one in certmanager.yaml) with a single line:

{{- $tlsEnabled := include "qdrant.tls.enabled" . | toBool -}}

Comment on lines +1 to +7
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

cd packages/apps/qdrant

cat > /tmp/qdrant-values-null.yaml <<'YAML'
external: true
tls:
  enabled: null
YAML

# Verify rendered value under null is inherited from external (expected: true)
helm template test-qdrant . -f /tmp/qdrant-values-null.yaml --show-only templates/qdrant.yaml \
  | rg -n -C2 'enable_tls|additionalVolumes|additionalVolumeMounts'

Repository: cozystack/cozystack

Length of output: 107


🏁 Script executed:

# First, examine the qdrant template file
cd packages/apps/qdrant
find . -name "qdrant.yaml" -type f

Repository: cozystack/cozystack

Length of output: 87


🏁 Script executed:

# Read the qdrant template file with line numbers
cat -n packages/apps/qdrant/templates/qdrant.yaml

Repository: cozystack/cozystack

Length of output: 2571


🏁 Script executed:

# Check the values structure in this chart
ls -la packages/apps/qdrant/
cat packages/apps/qdrant/values.yaml | head -50

Repository: cozystack/cozystack

Length of output: 1904


🏁 Script executed:

# Check the values.yaml to see default tls structure
cat packages/apps/qdrant/values.yaml

Repository: cozystack/cozystack

Length of output: 2415


🏁 Script executed:

# Look for values.schema.json to understand the expected types
cat packages/apps/qdrant/values.schema.json

Repository: cozystack/cozystack

Length of output: 3741


🌐 Web query:

Helm Go template null handling render "{{ null }}" output YAML

💡 Result:

In Helm templates (Go text/template), a value that is actually nil (“null” in YAML/values) does not reliably render as the literal string "null". Instead, Go/Helm ends up rendering a blank string (sometimes as a single space) when nil is expanded to text. This is why you may see YAML lines like key: instead of key: null, or weird whitespace. [1][2] What to do instead (robust patterns) 1) If you want to omit the field when the value is null/nil, guard with a nil check: {{- with .Values.someField }}{{ . }}{{- end }} or (if you need “only nil” semantics): {{- if kindIs "invalid" .Values.someField -}}...{{- end -}} Helm users commonly use with/else or explicit nil checks to avoid consuming nil values. [3] 2) If you are converting to YAML with toYaml, be aware that empty YAML scalars are parsed as Go nil, and toYaml will serialize nil as null according to YAML rules. In other words: this behavior comes from the input (empty scalars become nil) plus YAML serialization choice, not from the literal template string "{{ null }}". So to prevent inserting null, ensure the source values do not contain nils (e.g., set fields to {} or "" as appropriate), or prune/omit nils before calling toYaml. [4] 3) If you truly want the literal token null in the rendered output, don’t pass nil—pass the string "null": value: {{ "null" }} Because nil is treated as a nil value and stringification of nil yields an empty/blank result rather than the token null. [1][2] Practical mapping to your exact symptom - If your template contains something like ...: {{ null }} and the output is blank/space, that’s consistent with Go template nil rendering behavior (nil expands to whitespace/empty, not the text "null"). [2] - If your output instead contains null values inside YAML blocks generated by toYaml, then those nils were introduced earlier by YAML parsing of empty scalars in values, and toYaml is faithfully re-serializing nil as YAML null. [4] If you share a minimal snippet of your chart template + the relevant part of values.yaml, I can point to the exact fix (omit field vs force string "null" vs prune before toYaml).

Citations:


Handle tls.enabled: null as inherit-from-external, not as explicit override.

The values schema documents a tri-state contract: tls.enabled should inherit from .Values.external when unset or null, but the current code only checks key presence with hasKey. When tls.enabled is explicitly set to null, it passes the hasKey check, and index assigns nil to $tlsEnabled. This causes lines 53 and 56 to render enable_tls: (blank YAML), which violates the boolean contract and breaks Qdrant configuration.

Suggested fix
 {{- $tlsMap := default (dict) .Values.tls -}}
 {{- $tlsEnabled := false -}}
-{{- if hasKey $tlsMap "enabled" -}}
-  {{- $tlsEnabled = index $tlsMap "enabled" -}}
-{{- else -}}
+{{- $tlsRaw := index $tlsMap "enabled" -}}
+{{- if and (hasKey $tlsMap "enabled") (ne $tlsRaw nil) -}}
+  {{- $tlsEnabled = $tlsRaw -}}
+{{- else -}}
   {{- $tlsEnabled = .Values.external | default false -}}
 {{- end -}}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{{- $tlsMap := default (dict) .Values.tls -}}
{{- $tlsEnabled := false -}}
{{- if hasKey $tlsMap "enabled" -}}
{{- $tlsEnabled = index $tlsMap "enabled" -}}
{{- else -}}
{{- $tlsEnabled = .Values.external | default false -}}
{{- end -}}
{{- $tlsMap := default (dict) .Values.tls -}}
{{- $tlsEnabled := false -}}
{{- $tlsRaw := index $tlsMap "enabled" -}}
{{- if and (hasKey $tlsMap "enabled") (ne $tlsRaw nil) -}}
{{- $tlsEnabled = $tlsRaw -}}
{{- else -}}
{{- $tlsEnabled = .Values.external | default false -}}
{{- end -}}
🧰 Tools
🪛 YAMLlint (1.38.0)

[error] 1-1: syntax error: expected the node content, but found '-'

(syntax)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/apps/qdrant/templates/qdrant.yaml` around lines 1 - 7, The template
treats tls.enabled present-but-null as an explicit value; change the assignment
logic for $tlsEnabled so null falls back to .Values.external (and then to false)
instead of being used as-is. Replace the hasKey/index branch with a single safe
default expression that uses index $tlsMap "enabled" but falls back to
.Values.external (and then false) when nil—for example use Helm's default
function: set $tlsEnabled = default (.Values.external | default false) (index
$tlsMap "enabled") so null behaves like unset.

apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
Expand Down Expand Up @@ -41,3 +48,28 @@ spec:
service:
type: LoadBalancer
{{- end }}
config:
service:
enable_tls: {{ $tlsEnabled }}
cluster:
p2p:
enable_tls: {{ $tlsEnabled }}
{{- if $tlsEnabled }}
tls:
cert: /qdrant/tls/tls.crt
key: /qdrant/tls/tls.key
ca_cert: /qdrant/tls/ca.crt
{{- end }}
{{- if $tlsEnabled }}
additionalVolumes:
- name: tls
secret:
secretName: {{ .Release.Name }}-tls
additionalVolumeMounts:
- name: tls
mountPath: /qdrant/tls
readOnly: true
{{- else }}
additionalVolumes: []
additionalVolumeMounts: []
{{- end }}
Comment on lines +63 to +75
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

This if/else block can be simplified. Since the upstream Qdrant chart likely defines empty lists as defaults for additionalVolumes and additionalVolumeMounts, the else block which sets them to empty lists is redundant. You can remove it and only keep the if $tlsEnabled block. This makes the template cleaner and more concise.

      {{- if $tlsEnabled }}
      additionalVolumes:
        - name: tls
          secret:
            secretName: {{ .Release.Name }}-tls
      additionalVolumeMounts:
        - name: tls
          mountPath: /qdrant/tls
          readOnly: true
      {{- end }}

Loading
Loading