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
41 changes: 25 additions & 16 deletions charts/qdrant/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -73,21 +73,37 @@ Create the name of the service account to use
{{- end }}
{{- end }}

{{/*
Whether the api key is referenced from an existing secret via valueFrom
*/}}
{{- define "qdrant.apiKeyValueFrom" -}}
{{- if and (kindIs "map" .Values.apiKey) .Values.apiKey.valueFrom -}}true{{- end -}}
{{- end -}}

{{/*
Whether the read-only api key is referenced from an existing secret via valueFrom
*/}}
{{- define "qdrant.readOnlyApiKeyValueFrom" -}}
{{- if and (kindIs "map" .Values.readOnlyApiKey) .Values.readOnlyApiKey.valueFrom -}}true{{- end -}}
{{- end -}}

{{/*
Whether the chart creates its own <release>-apikey secret,
i.e. at least one api key is set as a string or generated (true), not referenced via valueFrom
*/}}
{{- define "qdrant.createApiKeySecret" -}}
{{- if or (and .Values.apiKey (not (include "qdrant.apiKeyValueFrom" .))) (and .Values.readOnlyApiKey (not (include "qdrant.readOnlyApiKeyValueFrom" .))) -}}true{{- end -}}
{{- end -}}

{{/*
Create secret
Api keys referenced via valueFrom are not part of it, they are passed to Qdrant as environment variables
*/}}
{{- define "qdrant.secret" -}}
{{- $readOnlyApiKey := false }}
{{- $apiKey := false }}
{{- if kindIs "map" .Values.apiKey -}}
{{- if .Values.apiKey.valueFrom -}}
{{- /* Retrieve the value from the secret as specified in valueFrom */ -}}
{{- $secretName := .Values.apiKey.valueFrom.secretKeyRef.name -}}
{{- $secretKey := .Values.apiKey.valueFrom.secretKeyRef.key -}}
{{- $secretObj := (lookup "v1" "Secret" .Release.Namespace $secretName) | default dict -}}
{{- $secretData := (get $secretObj "data") | default dict -}}
{{- $apiKey = (get $secretData $secretKey | b64dec) -}}
{{- end -}}
{{- /* valueFrom is passed to Qdrant as an environment variable, see statefulset.yaml */ -}}
{{- else if .Values.apiKey | toJson | eq "true" -}}
{{- /* Retrieve existing randomly generated api key or create a new one */ -}}
{{- $secretObj := (lookup "v1" "Secret" .Release.Namespace (printf "%s-apikey" (include "qdrant.fullname" . ))) | default dict -}}
Expand All @@ -97,14 +113,7 @@ Create secret
{{- $apiKey = .Values.apiKey -}}
{{- end -}}
{{- if kindIs "map" .Values.readOnlyApiKey -}}
{{- if .Values.readOnlyApiKey.valueFrom -}}
{{- /* Retrieve the value from the secret as specified in valueFrom */ -}}
{{- $secretName := .Values.readOnlyApiKey.valueFrom.secretKeyRef.name -}}
{{- $secretKey := .Values.readOnlyApiKey.valueFrom.secretKeyRef.key -}}
{{- $secretObj := (lookup "v1" "Secret" .Release.Namespace $secretName) | default dict -}}
{{- $secretData := (get $secretObj "data") | default dict -}}
{{- $readOnlyApiKey = (get $secretData $secretKey | b64dec) -}}
{{- end -}}
{{- /* valueFrom is passed to Qdrant as an environment variable, see statefulset.yaml */ -}}
{{- else if eq (.Values.readOnlyApiKey | toJson) "true" -}}
{{- /* retrieve existing randomly generated api key or create new one */ -}}
{{- $secretObj := (lookup "v1" "Secret" .Release.Namespace (printf "%s-apikey" (include "qdrant.fullname" . ))) | default dict -}}
Expand Down
2 changes: 1 addition & 1 deletion charts/qdrant/templates/secret.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{{- if or .Values.apiKey .Values.readOnlyApiKey }}
{{- if include "qdrant.createApiKeySecret" . }}
apiVersion: v1
kind: Secret
metadata:
Expand Down
8 changes: 8 additions & 0 deletions charts/qdrant/templates/servicemonitor.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,22 @@ spec:
authorization:
type: Bearer
credentials:
{{- if include "qdrant.readOnlyApiKeyValueFrom" . }}
{{- toYaml .Values.readOnlyApiKey.valueFrom.secretKeyRef | nindent 10 }}
{{- else }}
name: {{ include "qdrant.fullname" . }}-apikey
key: read-only-api-key
{{- end }}
{{- else if .Values.apiKey }}
authorization:
type: Bearer
credentials:
{{- if include "qdrant.apiKeyValueFrom" . }}
{{- toYaml .Values.apiKey.valueFrom.secretKeyRef | nindent 10 }}
{{- else }}
name: {{ include "qdrant.fullname" . }}-apikey
key: api-key
{{- end }}
{{- end }}
selector:
matchLabels:
Expand Down
14 changes: 11 additions & 3 deletions charts/qdrant/templates/statefulset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ spec:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
{{- if or .Values.apiKey .Values.readOnlyApiKey }}
{{- if include "qdrant.createApiKeySecret" . }}
checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
{{- end }}
{{- with .Values.podAnnotations }}
Expand Down Expand Up @@ -94,6 +94,14 @@ spec:
env:
- name: QDRANT_INIT_FILE_PATH
value: /qdrant/init/.qdrant-initialized
{{- if include "qdrant.apiKeyValueFrom" . }}
- name: QDRANT__SERVICE__API_KEY
valueFrom: {{- toYaml .Values.apiKey.valueFrom | nindent 16 }}
{{- end }}
{{- if include "qdrant.readOnlyApiKeyValueFrom" . }}
- name: QDRANT__SERVICE__READ_ONLY_API_KEY
valueFrom: {{- toYaml .Values.readOnlyApiKey.valueFrom | nindent 16 }}
{{- end }}
{{- range .Values.env }}
- name: {{ .name }}
{{- if .valueFrom }}
Expand Down Expand Up @@ -199,7 +207,7 @@ spec:
- name: qdrant-config
mountPath: /qdrant/config/production.yaml
subPath: production.yaml
{{- if or .Values.apiKey .Values.readOnlyApiKey }}
{{- if include "qdrant.createApiKeySecret" . }}
- name: qdrant-secret
mountPath: /qdrant/config/local.yaml
subPath: local.yaml
Expand Down Expand Up @@ -255,7 +263,7 @@ spec:
{{- end }}
- name: qdrant-init
emptyDir: {}
{{- if or .Values.apiKey .Values.readOnlyApiKey }}
{{- if include "qdrant.createApiKeySecret" . }}
- name: qdrant-secret
secret:
secretName: {{ include "qdrant.fullname" . }}-apikey
Expand Down
5 changes: 5 additions & 0 deletions test/integration/external_api_key.bats
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,9 @@ setup_file() {
run kubectl exec -n default curl -- curl -s http://qdrant.qdrant-helm-integration:6333/collections -H "api-key: test-read-only-api-key" --fail-with-body
[ $status -eq 0 ]
[[ "${output}" =~ .*\"status\":\"ok\".* ]]
}

@test "external api key authentication fails with no key" {
run kubectl exec -n default curl -- curl -s -w " - %{response_code}" http://qdrant.qdrant-helm-integration:6333/collections
[ "${output}" = "Must provide an API key or an Authorization bearer token - 401" ]
}
37 changes: 37 additions & 0 deletions test/integration/external_api_key_template.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
setup_file() {
# Using a custom namespace to avoid side effects with other tests
kubectl create ns qdrant-external-apikey-template
kubectl -n qdrant-external-apikey-template create secret generic qdrant-external-apikey --from-literal=apiKey=test-api-key --from-literal=readOnlyApiKey=test-read-only-api-key
# Render without cluster access and apply the output, like ArgoCD and other GitOps tools do.
# Helm's lookup function returns nothing in this mode.
helm template qdrant charts/qdrant -n qdrant-external-apikey-template --skip-tests \
--set apiKey.valueFrom.secretKeyRef.name=qdrant-external-apikey,apiKey.valueFrom.secretKeyRef.key=apiKey,readOnlyApiKey.valueFrom.secretKeyRef.name=qdrant-external-apikey,readOnlyApiKey.valueFrom.secretKeyRef.key=readOnlyApiKey \
| kubectl apply -n qdrant-external-apikey-template -f -
kubectl rollout status statefulset qdrant -n qdrant-external-apikey-template --timeout 3m
}

@test "external api key works when rendered with helm template" {
run kubectl exec -n default curl -- curl -s http://qdrant.qdrant-external-apikey-template:6333/collections -H "api-key: test-api-key" --fail-with-body
[ $status -eq 0 ]
[[ "${output}" =~ .*\"status\":\"ok\".* ]]
}

@test "external read only api key works when rendered with helm template" {
run kubectl exec -n default curl -- curl -s http://qdrant.qdrant-external-apikey-template:6333/collections -H "api-key: test-read-only-api-key" --fail-with-body
[ $status -eq 0 ]
[[ "${output}" =~ .*\"status\":\"ok\".* ]]
}

@test "external api key authentication fails with no key when rendered with helm template" {
run kubectl exec -n default curl -- curl -s -w " - %{response_code}" http://qdrant.qdrant-external-apikey-template:6333/collections
[ "${output}" = "Must provide an API key or an Authorization bearer token - 401" ]
}

@test "external api key authentication fails with wrong key when rendered with helm template" {
run kubectl exec -n default curl -- curl -s -w " - %{response_code}" http://qdrant.qdrant-external-apikey-template:6333/collections -H 'api-key: invalid'
[ "${output}" = "Invalid API key or JWT - 401" ]
}

teardown_file() {
kubectl delete ns qdrant-external-apikey-template
}