diff --git a/braintrust/ci/values-azure.yaml b/braintrust/ci/values-azure.yaml index 3a0d622..2be4bc8 100644 --- a/braintrust/ci/values-azure.yaml +++ b/braintrust/ci/values-azure.yaml @@ -84,6 +84,9 @@ brainstore: nodeSelector: {} tolerations: [] affinity: {} + fastreader: + volume: + size: "100Gi" writer: name: "brainstore-writer" replicas: 1 diff --git a/braintrust/examples/google-autopilot/values.yaml b/braintrust/examples/google-autopilot/values.yaml index 3865408..895d79b 100644 --- a/braintrust/examples/google-autopilot/values.yaml +++ b/braintrust/examples/google-autopilot/values.yaml @@ -6,7 +6,7 @@ global: namespace: "braintrust" # Cloud provider configuration -cloud: "google" +cloud: "google" # Google Cloud specific configuration for Autopilot google: @@ -58,7 +58,7 @@ brainstore: #image: #repository: public.ecr.aws/braintrust/brainstore #tag: "" - # New deployments should use objectStorage as the locks backend. Existing deployments should remain on redis at this time. + # New deployments should use objectStorage as the locks backend. Existing deployments should remain on redis at this time. locksBackend: "objectStorage" # Brainstore Reader configuration @@ -79,12 +79,36 @@ brainstore: memory: "32Gi" cacheDir: "/mnt/tmp/brainstore" objectStoreCacheMemoryLimit: "1Gi" - objectStoreCacheFileSize: "1000Gi" + objectStoreCacheFileSize: "900Gi" verbose: true volume: - size: "200Gi" + size: "1000Gi" extraEnvVars: - + + # Brainstore Fast Reader configuration + fastreader: + name: "brainstore-fastreader" + replicas: 2 + service: + name: "" + type: ClusterIP + port: 4000 + portName: http + resources: + requests: + cpu: "16" + memory: "32Gi" + limits: + cpu: "16" + memory: "32Gi" + cacheDir: "/mnt/tmp/brainstore" + objectStoreCacheMemoryLimit: "1Gi" + objectStoreCacheFileSize: "900Gi" + verbose: true + volume: + size: "1000Gi" + extraEnvVars: + # Brainstore Writer configuration writer: name: "brainstore-writer" @@ -103,9 +127,8 @@ brainstore: memory: "64Gi" cacheDir: "/mnt/tmp/brainstore" objectStoreCacheMemoryLimit: "1Gi" - objectStoreCacheFileSize: "1000Gi" + objectStoreCacheFileSize: "900Gi" verbose: true volume: - size: "200Gi" + size: "1000Gi" extraEnvVars: - \ No newline at end of file diff --git a/braintrust/templates/_helpers.tpl b/braintrust/templates/_helpers.tpl index 3509639..88322b2 100644 --- a/braintrust/templates/_helpers.tpl +++ b/braintrust/templates/_helpers.tpl @@ -8,3 +8,19 @@ Get the namespace to use for resources {{- .Release.Namespace -}} {{- end -}} {{- end -}} + +{{/* +Static fast reader query sources used by API. +*/}} +{{- define "braintrust.fastReaderQuerySourcesCsv" -}} +{{- $sources := list + "summaryPaginatedObjectViewer [realtime]" + "summaryPaginatedObjectViewer" + "a602c972-1843-4ee1-b6bc-d3c1075cd7e7" + "traceQueryFn-id" + "traceQueryFn-rootSpanId" + "fullSpanQueryFn-root_span_id" + "fullSpanQueryFn-id" +-}} +{{- join "," $sources -}} +{{- end -}} diff --git a/braintrust/templates/api-configmap.yaml b/braintrust/templates/api-configmap.yaml index 8c40187..f6414a8 100644 --- a/braintrust/templates/api-configmap.yaml +++ b/braintrust/templates/api-configmap.yaml @@ -42,6 +42,8 @@ data: BRAINSTORE_ENABLED: "true" BRAINSTORE_DEFAULT: "force" BRAINSTORE_URL: "http://{{ .Values.brainstore.reader.service.name | default .Values.brainstore.reader.name }}.{{ .Values.global.namespace }}:{{ .Values.brainstore.reader.service.port }}" + BRAINSTORE_FAST_READER_URL: "http://{{ .Values.brainstore.fastreader.service.name | default .Values.brainstore.fastreader.name }}.{{ .Values.global.namespace }}:{{ .Values.brainstore.fastreader.service.port }}" + BRAINSTORE_FAST_READER_QUERY_SOURCES: {{ include "braintrust.fastReaderQuerySourcesCsv" . | quote }} BRAINSTORE_WRITER_URL: "http://{{ .Values.brainstore.writer.service.name | default .Values.brainstore.writer.name }}.{{ .Values.global.namespace }}:{{ .Values.brainstore.writer.service.port }}" BRAINSTORE_BACKFILL_DISABLE_HISTORICAL: {{ .Values.api.backfillDisableHistorical | quote }} BRAINSTORE_BACKFILL_DISABLE_NONHISTORICAL: {{ .Values.api.backfillDisableNonhistorical | quote }} diff --git a/braintrust/templates/brainstore-fastreader-configmap.yaml b/braintrust/templates/brainstore-fastreader-configmap.yaml new file mode 100644 index 0000000..0faec08 --- /dev/null +++ b/braintrust/templates/brainstore-fastreader-configmap.yaml @@ -0,0 +1,47 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Values.brainstore.fastreader.name }} + namespace: {{ include "braintrust.namespace" . }} + {{- with (merge (deepCopy .Values.brainstore.fastreader.labels) .Values.global.labels) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.brainstore.fastreader.annotations.configmap }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +data: + BRAINSTORE_READER_ONLY_MODE: "true" + BRAINSTORE_VERBOSE: {{ (or (eq (toString .Values.brainstore.fastreader.verbose) "true") (eq (toString .Values.brainstore.fastreader.verbose) "1")) | ternary "1" "0" | quote }} + BRAINSTORE_PORT: {{ .Values.brainstore.fastreader.service.port | quote }} + BRAINSTORE_CACHE_DIR: {{ .Values.brainstore.fastreader.cacheDir | quote }} + BRAINSTORE_OBJECT_STORE_CACHE_MEMORY_LIMIT: {{ .Values.brainstore.fastreader.objectStoreCacheMemoryLimit | quote }} + BRAINSTORE_OBJECT_STORE_CACHE_FILE_SIZE: {{ .Values.brainstore.fastreader.objectStoreCacheFileSize | quote }} + {{- if eq .Values.cloud "azure" }} + # See here for reference: + # https://docs.rs/object_store/latest/object_store/azure/struct.MicrosoftAzureBuilder.html + AZURE_STORAGE_ACCOUNT_NAME: {{ .Values.objectStorage.azure.storageAccountName | quote }} + # This will create odd paths like az://brainstore/brainstore/index, but it maintains the naming and prefix conventions + # with AWS and avoids confusion with env vars. + BRAINSTORE_INDEX_URI: "az://{{ .Values.objectStorage.azure.brainstoreContainer }}/brainstore/index" + BRAINSTORE_REALTIME_WAL_URI: "az://{{ .Values.objectStorage.azure.brainstoreContainer }}/brainstore/wal" + {{- if eq .Values.brainstore.locksBackend "objectStorage" }} + BRAINSTORE_LOCKS_URI: "az://{{ .Values.objectStorage.azure.brainstoreContainer }}/brainstore/locks" + {{- end }} + {{- else if eq .Values.cloud "aws" }} + BRAINSTORE_INDEX_URI: "s3://{{ .Values.objectStorage.aws.brainstoreBucket }}/brainstore/index" + BRAINSTORE_REALTIME_WAL_URI: "s3://{{ .Values.objectStorage.aws.brainstoreBucket }}/brainstore/wal" + {{- if eq .Values.brainstore.locksBackend "objectStorage" }} + BRAINSTORE_LOCKS_URI: "s3://{{ .Values.objectStorage.aws.brainstoreBucket }}/brainstore/locks" + {{- end }} + {{- else if eq .Values.cloud "google" }} + BRAINSTORE_INDEX_URI: "gs://{{ .Values.objectStorage.google.brainstoreBucket }}/brainstore/index" + BRAINSTORE_REALTIME_WAL_URI: "gs://{{ .Values.objectStorage.google.brainstoreBucket }}/brainstore/wal" + {{- if eq .Values.brainstore.locksBackend "objectStorage" }} + BRAINSTORE_LOCKS_URI: "gs://{{ .Values.objectStorage.google.brainstoreBucket }}/brainstore/locks" + {{- end }} + {{- end }} + BRAINSTORE_CONTROL_PLANE_TELEMETRY: {{ .Values.global.controlPlaneTelemetry | quote }} + NO_COLOR: "1" diff --git a/braintrust/templates/brainstore-fastreader-deployment.yaml b/braintrust/templates/brainstore-fastreader-deployment.yaml new file mode 100644 index 0000000..cc43dd6 --- /dev/null +++ b/braintrust/templates/brainstore-fastreader-deployment.yaml @@ -0,0 +1,173 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Values.brainstore.fastreader.name }} + namespace: {{ include "braintrust.namespace" . }} + {{- with (merge (deepCopy .Values.brainstore.fastreader.labels) .Values.global.labels) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.brainstore.fastreader.annotations.deployment }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.brainstore.fastreader.replicas }} + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 100% + maxUnavailable: 0 + selector: + matchLabels: + app: {{ .Values.brainstore.fastreader.name }} + template: + metadata: + labels: + app: {{ .Values.brainstore.fastreader.name }} + {{- if eq .Values.cloud "azure" }} + azure.workload.identity/use: "true" + {{- end }} + {{- if eq .Values.cloud "google" }} + gke-workload-identity/use: "true" + {{- end }} + {{- with (merge (deepCopy .Values.brainstore.fastreader.podLabels) .Values.brainstore.fastreader.labels .Values.global.labels) }} + {{- toYaml . | nindent 8 }} + {{- end }} + annotations: + {{- if eq .Values.cloud "google" }} + iam.gke.io/gcp-service-account: {{ required "brainstore.serviceAccount.googleServiceAccount is required when cloud is google" .Values.brainstore.serviceAccount.googleServiceAccount }} + {{- end }} + {{- with .Values.brainstore.fastreader.annotations.pod }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ .Values.brainstore.serviceAccount.name }} + {{- if or .Values.brainstore.fastreader.nodeSelector (and (eq .Values.cloud "google") (eq .Values.google.mode "autopilot")) }} + nodeSelector: + {{- with .Values.brainstore.fastreader.nodeSelector }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if and (eq .Values.cloud "google") (eq .Values.google.mode "autopilot") }} + cloud.google.com/machine-family: {{ .Values.google.autopilotMachineFamily | default "c4" }} + cloud.google.com/gke-ephemeral-storage-local-ssd: "true" + cloud.google.com/compute-class: "Performance" + {{- end }} + {{- end }} + {{- with .Values.brainstore.fastreader.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.brainstore.fastreader.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: brainstore-fastreader + image: "{{ .Values.brainstore.image.repository }}:{{ .Values.brainstore.image.tag }}" + imagePullPolicy: {{ .Values.brainstore.image.pullPolicy }} + command: ["brainstore"] + args: ["web"] + ports: + - containerPort: {{ .Values.brainstore.fastreader.service.port }} + resources: + requests: + cpu: {{ .Values.brainstore.fastreader.resources.requests.cpu | quote }} + memory: {{ .Values.brainstore.fastreader.resources.requests.memory | quote }} + {{- if and (eq .Values.cloud "google") (eq .Values.google.mode "autopilot") .Values.brainstore.fastreader.volume.size }} + ephemeral-storage: {{ .Values.brainstore.fastreader.volume.size | quote }} + {{- end }} + limits: + cpu: {{ .Values.brainstore.fastreader.resources.limits.cpu | quote }} + memory: {{ .Values.brainstore.fastreader.resources.limits.memory | quote }} + {{- with .Values.brainstore.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.brainstore.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + envFrom: + - configMapRef: + name: {{ .Values.brainstore.fastreader.name }} + env: + - name: BRAINSTORE_METADATA_URI + valueFrom: + secretKeyRef: + name: braintrust-secrets + key: PG_URL + - name: BRAINSTORE_WAL_URI + valueFrom: + secretKeyRef: + name: braintrust-secrets + key: PG_URL + - name: BRAINSTORE_REDIS_URI + valueFrom: + secretKeyRef: + name: braintrust-secrets + key: REDIS_URL + - name: BRAINSTORE_XACT_MANAGER_URI + valueFrom: + secretKeyRef: + name: braintrust-secrets + key: REDIS_URL + - name: BRAINSTORE_AI_PROXY_URL + value: "http://{{ .Values.api.service.name | default .Values.api.name }}:{{ .Values.api.service.port }}" + {{- if eq .Values.brainstore.locksBackend "redis" }} + - name: BRAINSTORE_LOCKS_URI + valueFrom: + secretKeyRef: + name: braintrust-secrets + key: REDIS_URL + {{- end }} + - name: BRAINSTORE_LICENSE_KEY + valueFrom: + secretKeyRef: + name: braintrust-secrets + key: BRAINSTORE_LICENSE_KEY + - name: FUNCTION_SECRET_KEY + valueFrom: + secretKeyRef: + name: braintrust-secrets + key: FUNCTION_SECRET_KEY + {{- if .Values.brainstore.fastreader.extraEnvVars }} + {{- toYaml .Values.brainstore.fastreader.extraEnvVars | nindent 12 }} + {{- end }} + volumeMounts: + - name: cache-volume + mountPath: {{ .Values.brainstore.fastreader.cacheDir }} + {{- if and (eq .Values.cloud "azure") .Values.azure.enableAzureKeyVaultDriver }} + - name: secrets-store-inline + mountPath: "/mnt/secrets-store" + readOnly: true + {{- end }} + {{- with .Values.brainstore.fastreader.extraContainers }} + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: cache-volume + {{- if and (eq .Values.cloud "azure") .Values.azure.enableAzureContainerStorageDriver }} + ephemeral: + volumeClaimTemplate: + spec: + volumeMode: Filesystem + accessModes: ["ReadWriteOnce"] + storageClassName: local + resources: + requests: + storage: {{ required "brainstore.fastreader.volume.size must be set" .Values.brainstore.fastreader.volume.size | quote }} + {{- else }} + emptyDir: {} + {{- end }} + {{- if and (eq .Values.cloud "azure") .Values.azure.enableAzureKeyVaultDriver }} + - name: secrets-store-inline + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: {{ .Values.azure.keyVaultName }} + {{- end }} + {{- with .Values.brainstore.fastreader.extraVolumes }} + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/braintrust/templates/brainstore-fastreader-service.yaml b/braintrust/templates/brainstore-fastreader-service.yaml new file mode 100644 index 0000000..cbff013 --- /dev/null +++ b/braintrust/templates/brainstore-fastreader-service.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.brainstore.fastreader.service.name | default .Values.brainstore.fastreader.name }} + namespace: {{ include "braintrust.namespace" . }} + {{- with (merge (deepCopy .Values.brainstore.fastreader.labels) .Values.global.labels) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.brainstore.fastreader.annotations.service }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + app: {{ .Values.brainstore.fastreader.name }} + ports: + - name: {{ .Values.brainstore.fastreader.service.portName }} + protocol: TCP + port: {{ .Values.brainstore.fastreader.service.port }} + targetPort: {{ .Values.brainstore.fastreader.service.port }} + type: {{ .Values.brainstore.fastreader.service.type }} diff --git a/braintrust/tests/__fixtures__/base-values.yaml b/braintrust/tests/__fixtures__/base-values.yaml index b485e11..c7fcb07 100644 --- a/braintrust/tests/__fixtures__/base-values.yaml +++ b/braintrust/tests/__fixtures__/base-values.yaml @@ -89,6 +89,38 @@ brainstore: nodeSelector: {} tolerations: [] affinity: {} + fastreader: + name: "brainstore-fastreader" + labels: {} + podLabels: {} + annotations: + configmap: {} + deployment: {} + service: {} + pod: {} + replicas: 1 + service: + name: "" + type: ClusterIP + port: 4000 + portName: http + resources: + requests: + cpu: "4" + memory: "8Gi" + limits: + cpu: "8" + memory: "16Gi" + cacheDir: "/mnt/tmp/brainstore" + objectStoreCacheMemoryLimit: "1Gi" + objectStoreCacheFileSize: "50Gi" + verbose: true + volume: + size: "100Gi" + extraEnvVars: [] + nodeSelector: {} + tolerations: [] + affinity: {} writer: name: "brainstore-writer" labels: {} diff --git a/braintrust/tests/api-configmap_test.yaml b/braintrust/tests/api-configmap_test.yaml index 5d206bd..e546c32 100644 --- a/braintrust/tests/api-configmap_test.yaml +++ b/braintrust/tests/api-configmap_test.yaml @@ -124,6 +124,12 @@ tests: - equal: path: data.BRAINSTORE_URL value: "http://brainstore-reader.braintrust:4000" + - equal: + path: data.BRAINSTORE_FAST_READER_URL + value: "http://brainstore-fastreader.braintrust:4000" + - equal: + path: data.BRAINSTORE_FAST_READER_QUERY_SOURCES + value: "summaryPaginatedObjectViewer [realtime],summaryPaginatedObjectViewer,a602c972-1843-4ee1-b6bc-d3c1075cd7e7,traceQueryFn-id,traceQueryFn-rootSpanId,fullSpanQueryFn-root_span_id,fullSpanQueryFn-id" - equal: path: data.BRAINSTORE_WRITER_URL value: "http://brainstore-writer.braintrust:4000" diff --git a/braintrust/tests/brainstore-fastreader-configmap_test.yaml b/braintrust/tests/brainstore-fastreader-configmap_test.yaml new file mode 100644 index 0000000..3bc9e11 --- /dev/null +++ b/braintrust/tests/brainstore-fastreader-configmap_test.yaml @@ -0,0 +1,34 @@ +suite: test Brainstore Fast Reader configmap template +templates: + - brainstore-fastreader-configmap.yaml +tests: + - it: should render Brainstore Fast Reader configmap with default values + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: brainstore-fastreader + - equal: + path: data.BRAINSTORE_READER_ONLY_MODE + value: "true" + - equal: + path: data.BRAINSTORE_PORT + value: "4000" + + - it: should include configmap annotations when provided + values: + - __fixtures__/base-values.yaml + set: + brainstore.fastreader.annotations.configmap: + custom-annotation: "value" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.annotations["custom-annotation"] + value: value diff --git a/braintrust/tests/brainstore-fastreader-service_test.yaml b/braintrust/tests/brainstore-fastreader-service_test.yaml new file mode 100644 index 0000000..77cc34f --- /dev/null +++ b/braintrust/tests/brainstore-fastreader-service_test.yaml @@ -0,0 +1,30 @@ +suite: test Brainstore Fast Reader service template +templates: + - brainstore-fastreader-service.yaml +tests: + - it: should render Brainstore Fast Reader service with default values + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - isKind: + of: Service + - equal: + path: metadata.name + value: brainstore-fastreader + - equal: + path: spec.selector.app + value: brainstore-fastreader + + - it: should use custom service name when provided + values: + - __fixtures__/base-values.yaml + set: + brainstore.fastreader.service.name: "custom-fastreader-service" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.name + value: custom-fastreader-service diff --git a/braintrust/tests/brainstore-fastreader_test.yaml b/braintrust/tests/brainstore-fastreader_test.yaml new file mode 100644 index 0000000..c4c30a5 --- /dev/null +++ b/braintrust/tests/brainstore-fastreader_test.yaml @@ -0,0 +1,43 @@ +suite: test Brainstore Fast Reader deployment template +templates: + - brainstore-fastreader-deployment.yaml +tests: + - it: should render Brainstore Fast Reader deployment with default values + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - isKind: + of: Deployment + - equal: + path: metadata.name + value: brainstore-fastreader + - equal: + path: spec.replicas + value: 1 + - equal: + path: spec.template.spec.containers[0].name + value: brainstore-fastreader + - equal: + path: spec.template.spec.containers[0].image + value: test/brainstore:v1.0.0 + - equal: + path: spec.template.spec.containers[0].envFrom[0].configMapRef.name + value: brainstore-fastreader + + - it: should include extraEnvVars when provided + values: + - __fixtures__/base-values.yaml + set: + brainstore.fastreader.extraEnvVars: + - name: CUSTOM_VAR + value: "custom-value" + release: + namespace: "braintrust" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: CUSTOM_VAR + value: "custom-value" diff --git a/braintrust/tests/labels-merge-configmaps_test.yaml b/braintrust/tests/labels-merge-configmaps_test.yaml index e64c7d7..537db3a 100644 --- a/braintrust/tests/labels-merge-configmaps_test.yaml +++ b/braintrust/tests/labels-merge-configmaps_test.yaml @@ -3,6 +3,7 @@ templates: - api-configmap.yaml - brainstore-reader-configmap.yaml - brainstore-writer-configmap.yaml + - brainstore-fastreader-configmap.yaml tests: # These tests verify the deepCopy fix for configmaps. # Without deepCopy, labels would bleed between components due to merge mutation. @@ -15,6 +16,7 @@ tests: api.labels.api-specific: "api-cm" brainstore.reader.labels.reader-specific: "reader-cm" brainstore.writer.labels.writer-specific: "writer-cm" + brainstore.fastreader.labels.fastreader-specific: "fastreader-cm" release: namespace: "braintrust" template: api-configmap.yaml @@ -29,6 +31,8 @@ tests: path: metadata.labels["reader-specific"] - isNull: path: metadata.labels["writer-specific"] + - isNull: + path: metadata.labels["fastreader-specific"] - it: should give Reader configmap only global and reader-specific labels values: @@ -38,6 +42,7 @@ tests: api.labels.api-specific: "api-cm" brainstore.reader.labels.reader-specific: "reader-cm" brainstore.writer.labels.writer-specific: "writer-cm" + brainstore.fastreader.labels.fastreader-specific: "fastreader-cm" release: namespace: "braintrust" template: brainstore-reader-configmap.yaml @@ -52,6 +57,8 @@ tests: path: metadata.labels["api-specific"] - isNull: path: metadata.labels["writer-specific"] + - isNull: + path: metadata.labels["fastreader-specific"] - it: should give Writer configmap only global and writer-specific labels values: @@ -61,6 +68,7 @@ tests: api.labels.api-specific: "api-cm" brainstore.reader.labels.reader-specific: "reader-cm" brainstore.writer.labels.writer-specific: "writer-cm" + brainstore.fastreader.labels.fastreader-specific: "fastreader-cm" release: namespace: "braintrust" template: brainstore-writer-configmap.yaml @@ -75,3 +83,29 @@ tests: path: metadata.labels["api-specific"] - isNull: path: metadata.labels["reader-specific"] + + - it: should give Fastreader configmap only global and fastreader-specific labels + values: + - __fixtures__/base-values.yaml + set: + global.labels.shared: "global-cm" + api.labels.api-specific: "api-cm" + brainstore.reader.labels.reader-specific: "reader-cm" + brainstore.writer.labels.writer-specific: "writer-cm" + brainstore.fastreader.labels.fastreader-specific: "fastreader-cm" + release: + namespace: "braintrust" + template: brainstore-fastreader-configmap.yaml + asserts: + - equal: + path: metadata.labels.shared + value: global-cm + - equal: + path: metadata.labels["fastreader-specific"] + value: fastreader-cm + - isNull: + path: metadata.labels["api-specific"] + - isNull: + path: metadata.labels["reader-specific"] + - isNull: + path: metadata.labels["writer-specific"] diff --git a/braintrust/tests/labels-merge-isolation_test.yaml b/braintrust/tests/labels-merge-isolation_test.yaml index 1b5f965..32cabd5 100644 --- a/braintrust/tests/labels-merge-isolation_test.yaml +++ b/braintrust/tests/labels-merge-isolation_test.yaml @@ -3,10 +3,11 @@ templates: - api-deployment.yaml - brainstore-reader-deployment.yaml - brainstore-writer-deployment.yaml + - brainstore-fastreader-deployment.yaml tests: # This test verifies that labels are isolated between components. # The bug that was fixed: Helm's merge function mutates the leftmost dict, - # so without deepCopy, if api.labels, reader.labels, and writer.labels + # so without deepCopy, if api.labels, reader.labels, writer.labels, and fastreader.labels # are different, the last rendered component's labels would bleed into # earlier components through the mutated global.labels. # @@ -25,6 +26,8 @@ tests: brainstore.reader.labels.reader-specific: "reader-only" brainstore.writer.labels.component: "writer" brainstore.writer.labels.writer-specific: "writer-only" + brainstore.fastreader.labels.component: "fastreader" + brainstore.fastreader.labels.fastreader-specific: "fastreader-only" release: namespace: "braintrust" template: api-deployment.yaml @@ -43,11 +46,13 @@ tests: - equal: path: metadata.labels["api-specific"] value: api-only - # API deployment should NOT have reader or writer specific labels + # API deployment should NOT have reader, writer, or fastreader specific labels - isNull: path: metadata.labels["reader-specific"] - isNull: path: metadata.labels["writer-specific"] + - isNull: + path: metadata.labels["fastreader-specific"] # Pod template labels should also be isolated - equal: path: spec.template.metadata.labels.shared @@ -59,6 +64,8 @@ tests: path: spec.template.metadata.labels["reader-specific"] - isNull: path: spec.template.metadata.labels["writer-specific"] + - isNull: + path: spec.template.metadata.labels["fastreader-specific"] - it: should give Reader deployment only global and reader-specific labels values: @@ -72,6 +79,8 @@ tests: brainstore.reader.labels.reader-specific: "reader-only" brainstore.writer.labels.component: "writer" brainstore.writer.labels.writer-specific: "writer-only" + brainstore.fastreader.labels.component: "fastreader" + brainstore.fastreader.labels.fastreader-specific: "fastreader-only" release: namespace: "braintrust" template: brainstore-reader-deployment.yaml @@ -90,11 +99,13 @@ tests: - equal: path: metadata.labels["reader-specific"] value: reader-only - # Reader deployment should NOT have api or writer specific labels + # Reader deployment should NOT have api, writer, or fastreader specific labels - isNull: path: metadata.labels["api-specific"] - isNull: path: metadata.labels["writer-specific"] + - isNull: + path: metadata.labels["fastreader-specific"] # Pod template labels should also be isolated - equal: path: spec.template.metadata.labels.shared @@ -106,6 +117,8 @@ tests: path: spec.template.metadata.labels["api-specific"] - isNull: path: spec.template.metadata.labels["writer-specific"] + - isNull: + path: spec.template.metadata.labels["fastreader-specific"] - it: should give Writer deployment only global and writer-specific labels values: @@ -119,6 +132,8 @@ tests: brainstore.reader.labels.reader-specific: "reader-only" brainstore.writer.labels.component: "writer" brainstore.writer.labels.writer-specific: "writer-only" + brainstore.fastreader.labels.component: "fastreader" + brainstore.fastreader.labels.fastreader-specific: "fastreader-only" release: namespace: "braintrust" template: brainstore-writer-deployment.yaml @@ -137,11 +152,13 @@ tests: - equal: path: metadata.labels["writer-specific"] value: writer-only - # Writer deployment should NOT have api or reader specific labels + # Writer deployment should NOT have api, reader, or fastreader specific labels - isNull: path: metadata.labels["api-specific"] - isNull: path: metadata.labels["reader-specific"] + - isNull: + path: metadata.labels["fastreader-specific"] # Pod template labels should also be isolated - equal: path: spec.template.metadata.labels.shared @@ -153,6 +170,61 @@ tests: path: spec.template.metadata.labels["api-specific"] - isNull: path: spec.template.metadata.labels["reader-specific"] + - isNull: + path: spec.template.metadata.labels["fastreader-specific"] + + - it: should give Fastreader deployment only global and fastreader-specific labels + values: + - __fixtures__/base-values.yaml + set: + global.labels.shared: "global-value" + global.labels.environment: "production" + api.labels.component: "api" + api.labels.api-specific: "api-only" + brainstore.reader.labels.component: "reader" + brainstore.reader.labels.reader-specific: "reader-only" + brainstore.writer.labels.component: "writer" + brainstore.writer.labels.writer-specific: "writer-only" + brainstore.fastreader.labels.component: "fastreader" + brainstore.fastreader.labels.fastreader-specific: "fastreader-only" + release: + namespace: "braintrust" + template: brainstore-fastreader-deployment.yaml + asserts: + # Fastreader deployment should have global labels + - equal: + path: metadata.labels.shared + value: global-value + - equal: + path: metadata.labels.environment + value: production + # Fastreader deployment should have fastreader-specific labels + - equal: + path: metadata.labels.component + value: fastreader + - equal: + path: metadata.labels["fastreader-specific"] + value: fastreader-only + # Fastreader deployment should NOT have api, reader, or writer specific labels + - isNull: + path: metadata.labels["api-specific"] + - isNull: + path: metadata.labels["reader-specific"] + - isNull: + path: metadata.labels["writer-specific"] + # Pod template labels should also be isolated + - equal: + path: spec.template.metadata.labels.shared + value: global-value + - equal: + path: spec.template.metadata.labels["fastreader-specific"] + value: fastreader-only + - isNull: + path: spec.template.metadata.labels["api-specific"] + - isNull: + path: spec.template.metadata.labels["reader-specific"] + - isNull: + path: spec.template.metadata.labels["writer-specific"] - it: should propagate global labels to API without component labels values: @@ -205,6 +277,23 @@ tests: path: metadata.labels.team value: platform + - it: should propagate global labels to Fastreader without component labels + values: + - __fixtures__/base-values.yaml + set: + global.labels.environment: "staging" + global.labels.team: "platform" + release: + namespace: "braintrust" + template: brainstore-fastreader-deployment.yaml + asserts: + - equal: + path: metadata.labels.environment + value: staging + - equal: + path: metadata.labels.team + value: platform + # Note: Helm's merge function gives precedence to the destination (first argument). # With `merge (deepCopy .Values.component.labels) .Values.global.labels`, # component labels take precedence over global labels when keys conflict. @@ -269,3 +358,23 @@ tests: - equal: path: metadata.labels["writer-only"] value: writer-value + + - it: should give component labels precedence over global labels when keys conflict (Fastreader) + values: + - __fixtures__/base-values.yaml + set: + global.labels.tier: "global-tier" + brainstore.fastreader.labels.tier: "fastreader-tier" + brainstore.fastreader.labels.fastreader-only: "fastreader-value" + release: + namespace: "braintrust" + template: brainstore-fastreader-deployment.yaml + asserts: + # Component label takes precedence for conflicting key + - equal: + path: metadata.labels.tier + value: fastreader-tier + # Non-conflicting component label is still added + - equal: + path: metadata.labels["fastreader-only"] + value: fastreader-value diff --git a/braintrust/tests/labels-merge-services_test.yaml b/braintrust/tests/labels-merge-services_test.yaml index 0f47831..82992b8 100644 --- a/braintrust/tests/labels-merge-services_test.yaml +++ b/braintrust/tests/labels-merge-services_test.yaml @@ -3,6 +3,7 @@ templates: - api-service.yaml - brainstore-reader-service.yaml - brainstore-writer-service.yaml + - brainstore-fastreader-service.yaml tests: # These tests verify the deepCopy fix for services. # Without deepCopy, labels would bleed between components due to merge mutation. @@ -19,6 +20,8 @@ tests: brainstore.reader.labels.reader-specific: "reader-svc" brainstore.writer.labels.component: "writer" brainstore.writer.labels.writer-specific: "writer-svc" + brainstore.fastreader.labels.component: "fastreader" + brainstore.fastreader.labels.fastreader-specific: "fastreader-svc" release: namespace: "braintrust" template: api-service.yaml @@ -36,6 +39,8 @@ tests: path: metadata.labels["reader-specific"] - isNull: path: metadata.labels["writer-specific"] + - isNull: + path: metadata.labels["fastreader-specific"] - it: should give Reader service only global and reader-specific labels values: @@ -45,6 +50,7 @@ tests: api.labels.api-specific: "api-svc" brainstore.reader.labels.reader-specific: "reader-svc" brainstore.writer.labels.writer-specific: "writer-svc" + brainstore.fastreader.labels.fastreader-specific: "fastreader-svc" release: namespace: "braintrust" template: brainstore-reader-service.yaml @@ -59,6 +65,8 @@ tests: path: metadata.labels["api-specific"] - isNull: path: metadata.labels["writer-specific"] + - isNull: + path: metadata.labels["fastreader-specific"] - it: should give Writer service only global and writer-specific labels values: @@ -68,6 +76,7 @@ tests: api.labels.api-specific: "api-svc" brainstore.reader.labels.reader-specific: "reader-svc" brainstore.writer.labels.writer-specific: "writer-svc" + brainstore.fastreader.labels.fastreader-specific: "fastreader-svc" release: namespace: "braintrust" template: brainstore-writer-service.yaml @@ -82,3 +91,29 @@ tests: path: metadata.labels["api-specific"] - isNull: path: metadata.labels["reader-specific"] + + - it: should give Fastreader service only global and fastreader-specific labels + values: + - __fixtures__/base-values.yaml + set: + global.labels.shared: "global-service" + api.labels.api-specific: "api-svc" + brainstore.reader.labels.reader-specific: "reader-svc" + brainstore.writer.labels.writer-specific: "writer-svc" + brainstore.fastreader.labels.fastreader-specific: "fastreader-svc" + release: + namespace: "braintrust" + template: brainstore-fastreader-service.yaml + asserts: + - equal: + path: metadata.labels.shared + value: global-service + - equal: + path: metadata.labels["fastreader-specific"] + value: fastreader-svc + - isNull: + path: metadata.labels["api-specific"] + - isNull: + path: metadata.labels["reader-specific"] + - isNull: + path: metadata.labels["writer-specific"] diff --git a/braintrust/values.yaml b/braintrust/values.yaml index adfe264..da6713b 100644 --- a/braintrust/values.yaml +++ b/braintrust/values.yaml @@ -146,7 +146,7 @@ api: extraContainers: [] extraVolumes: [] -# Brainstore configuration (split into reader and writer) +# Brainstore configuration (split into reader/fastreader/writer) brainstore: # Shared configuration labels: {} @@ -227,6 +227,45 @@ brainstore: extraContainers: [] extraVolumes: [] + # Brainstore Fast Reader configuration + fastreader: + name: "brainstore-fastreader" + labels: {} + podLabels: {} + annotations: + configmap: {} + deployment: {} + service: {} + pod: {} + replicas: 2 + service: + name: "" + type: ClusterIP + port: 4000 + portName: http + resources: + requests: + cpu: "16" + memory: "32Gi" + limits: + cpu: "16" + memory: "32Gi" + cacheDir: "/mnt/tmp/brainstore" + objectStoreCacheMemoryLimit: "1Gi" + objectStoreCacheFileSize: "1000Gi" + verbose: true + # Optional: Volume configuration for cache storage + # When not set, uses default emptyDir: {} (backward compatible) + volume: + # Storage size for ephemeral storage requests (used with GKE Autopilot local SSDs) + size: "" + extraEnvVars: [] + nodeSelector: {} + tolerations: [] + affinity: {} + extraContainers: [] + extraVolumes: [] + # Brainstore Writer configuration writer: name: "brainstore-writer" @@ -319,4 +358,4 @@ virtualService: - destination: host: "braintrust-api" # Should match api.service.name or api.name port: - number: 8000 # Should match api.service.port \ No newline at end of file + number: 8000 # Should match api.service.port