diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..247fa93 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,84 @@ +# AI Agent Instructions + +This document provides guidelines for AI agents reviewing or modifying this Helm chart repository. + +## Testing Requirements + +### Running Tests + +```bash +./test.sh +``` + +This runs: +1. Unit tests via `helm-unittest` +2. Template rendering tests for all cloud providers +3. Helm lint + +### When to Add Tests + +- Any new template that uses label merging must have corresponding isolation tests +- New features should have unit test coverage +- Test fixtures are in `braintrust/tests/__fixtures__/` + +### Test File Naming + +- Template tests: `_test.yaml` +- Cross-template tests: `-*_test.yaml` + +## Template Guidelines + +### Cloud Provider Conditionals + +This chart supports multiple cloud providers. Use conditionals appropriately: + +```yaml +{{- if eq .Values.cloud "google" }} +# Google-specific configuration +{{- end }} + +{{- if eq .Values.cloud "azure" }} +# Azure-specific configuration +{{- end }} + +{{- if eq .Values.cloud "aws" }} +# AWS-specific configuration +{{- end }} +``` + +### Required Values + +Use `required` for values that must be set for specific configurations: + +```yaml +{{ required "brainstore.serviceAccount.googleServiceAccount is required when cloud is google" .Values.brainstore.serviceAccount.googleServiceAccount }} +``` + +### Namespace Handling + +Always use the namespace helper: + +```yaml +namespace: {{ include "braintrust.namespace" . }} +``` + +## Review guidelines + +When reviewing PRs, verify: + +- [ ] Any `merge` on dicts uses `deepCopy` wrapper to ensure the original dict is not mutated +- [ ] New templates follow existing patterns +- [ ] Tests are added for new functionality +- [ ] Cloud-specific code is properly conditioned + +## File Structure + +``` +braintrust/ +├── templates/ # Kubernetes manifest templates +├── tests/ # helm-unittest test files +│ └── __fixtures__/ # Test value fixtures +├── ci/ # CI value files for different clouds +├── values.yaml # Default values +└── Chart.yaml # Chart metadata +``` diff --git a/braintrust/templates/api-configmap.yaml b/braintrust/templates/api-configmap.yaml index 9b090d3..0482bfa 100644 --- a/braintrust/templates/api-configmap.yaml +++ b/braintrust/templates/api-configmap.yaml @@ -4,7 +4,7 @@ kind: ConfigMap metadata: name: {{ .Values.api.name }} namespace: {{ include "braintrust.namespace" . }} - {{- with (merge .Values.global.labels .Values.api.labels) }} + {{- with (merge (deepCopy .Values.global.labels) .Values.api.labels) }} labels: {{- toYaml . | nindent 4 }} {{- end }} diff --git a/braintrust/templates/api-deployment.yaml b/braintrust/templates/api-deployment.yaml index b01e4df..847fe32 100644 --- a/braintrust/templates/api-deployment.yaml +++ b/braintrust/templates/api-deployment.yaml @@ -3,7 +3,7 @@ kind: Deployment metadata: name: {{ .Values.api.name }} namespace: {{ include "braintrust.namespace" . }} - {{- with (merge .Values.global.labels .Values.api.labels) }} + {{- with (merge (deepCopy .Values.global.labels) .Values.api.labels) }} labels: {{- toYaml . | nindent 4 }} {{- end }} @@ -31,7 +31,7 @@ spec: {{- if and (eq .Values.cloud "google") .Values.api.enableGcsAuth }} gke-workload-identity/use: "true" {{- end }} - {{- with (merge .Values.global.labels .Values.api.labels) }} + {{- with (merge (deepCopy .Values.global.labels) .Values.api.labels) }} {{- toYaml . | nindent 8 }} {{- end }} annotations: diff --git a/braintrust/templates/api-service.yaml b/braintrust/templates/api-service.yaml index 9e761ee..5d4b437 100644 --- a/braintrust/templates/api-service.yaml +++ b/braintrust/templates/api-service.yaml @@ -3,7 +3,7 @@ kind: Service metadata: name: {{ .Values.api.service.name | default .Values.api.name }} namespace: {{ include "braintrust.namespace" . }} - {{- with (merge .Values.global.labels .Values.api.labels) }} + {{- with (merge (deepCopy .Values.global.labels) .Values.api.labels) }} labels: {{- toYaml . | nindent 4 }} {{- end }} diff --git a/braintrust/templates/api-serviceaccount.yaml b/braintrust/templates/api-serviceaccount.yaml index 6645e32..01127a6 100644 --- a/braintrust/templates/api-serviceaccount.yaml +++ b/braintrust/templates/api-serviceaccount.yaml @@ -3,7 +3,7 @@ kind: ServiceAccount metadata: name: {{ .Values.api.serviceAccount.name }} namespace: {{ include "braintrust.namespace" . }} - {{- with (merge .Values.global.labels .Values.api.labels) }} + {{- with (merge (deepCopy .Values.global.labels) .Values.api.labels) }} labels: {{- toYaml . | nindent 4 }} {{- end }} diff --git a/braintrust/templates/brainstore-reader-configmap.yaml b/braintrust/templates/brainstore-reader-configmap.yaml index 7b30d44..d6f3d08 100644 --- a/braintrust/templates/brainstore-reader-configmap.yaml +++ b/braintrust/templates/brainstore-reader-configmap.yaml @@ -4,7 +4,7 @@ kind: ConfigMap metadata: name: {{ .Values.brainstore.reader.name }} namespace: {{ include "braintrust.namespace" . }} - {{- with (merge .Values.global.labels .Values.brainstore.reader.labels) }} + {{- with (merge (deepCopy .Values.global.labels) .Values.brainstore.reader.labels) }} labels: {{- toYaml . | nindent 4 }} {{- end }} diff --git a/braintrust/templates/brainstore-reader-deployment.yaml b/braintrust/templates/brainstore-reader-deployment.yaml index 3a5a3c6..960a0f4 100644 --- a/braintrust/templates/brainstore-reader-deployment.yaml +++ b/braintrust/templates/brainstore-reader-deployment.yaml @@ -3,7 +3,7 @@ kind: Deployment metadata: name: {{ .Values.brainstore.reader.name }} namespace: {{ include "braintrust.namespace" . }} - {{- with (merge .Values.global.labels .Values.brainstore.reader.labels) }} + {{- with (merge (deepCopy .Values.global.labels) .Values.brainstore.reader.labels) }} labels: {{- toYaml . | nindent 4 }} {{- end }} @@ -31,7 +31,7 @@ spec: {{- if eq .Values.cloud "google" }} gke-workload-identity/use: "true" {{- end }} - {{- with (merge .Values.global.labels .Values.brainstore.reader.labels) }} + {{- with (merge (deepCopy .Values.global.labels) .Values.brainstore.reader.labels) }} {{- toYaml . | nindent 8 }} {{- end }} annotations: diff --git a/braintrust/templates/brainstore-reader-service.yaml b/braintrust/templates/brainstore-reader-service.yaml index 9d259c3..2bb5451 100644 --- a/braintrust/templates/brainstore-reader-service.yaml +++ b/braintrust/templates/brainstore-reader-service.yaml @@ -3,7 +3,7 @@ kind: Service metadata: name: {{ .Values.brainstore.reader.service.name | default .Values.brainstore.reader.name }} namespace: {{ include "braintrust.namespace" . }} - {{- with (merge .Values.global.labels .Values.brainstore.reader.labels) }} + {{- with (merge (deepCopy .Values.global.labels) .Values.brainstore.reader.labels) }} labels: {{- toYaml . | nindent 4 }} {{- end }} diff --git a/braintrust/templates/brainstore-serviceaccount.yaml b/braintrust/templates/brainstore-serviceaccount.yaml index 2cabcd2..b8ad15a 100644 --- a/braintrust/templates/brainstore-serviceaccount.yaml +++ b/braintrust/templates/brainstore-serviceaccount.yaml @@ -3,7 +3,7 @@ kind: ServiceAccount metadata: name: {{ .Values.brainstore.serviceAccount.name }} namespace: {{ include "braintrust.namespace" . }} - {{- with (merge .Values.global.labels .Values.brainstore.labels) }} + {{- with (merge (deepCopy .Values.global.labels) .Values.brainstore.labels) }} labels: {{- toYaml . | nindent 4 }} {{- end }} diff --git a/braintrust/templates/brainstore-writer-configmap.yaml b/braintrust/templates/brainstore-writer-configmap.yaml index 649f7e5..d934491 100644 --- a/braintrust/templates/brainstore-writer-configmap.yaml +++ b/braintrust/templates/brainstore-writer-configmap.yaml @@ -4,7 +4,7 @@ kind: ConfigMap metadata: name: {{ .Values.brainstore.writer.name }} namespace: {{ include "braintrust.namespace" . }} - {{- with (merge .Values.global.labels .Values.brainstore.writer.labels) }} + {{- with (merge (deepCopy .Values.global.labels) .Values.brainstore.writer.labels) }} labels: {{- toYaml . | nindent 4 }} {{- end }} diff --git a/braintrust/templates/brainstore-writer-deployment.yaml b/braintrust/templates/brainstore-writer-deployment.yaml index b756937..9fe0c09 100644 --- a/braintrust/templates/brainstore-writer-deployment.yaml +++ b/braintrust/templates/brainstore-writer-deployment.yaml @@ -3,7 +3,7 @@ kind: Deployment metadata: name: {{ .Values.brainstore.writer.name }} namespace: {{ include "braintrust.namespace" . }} - {{- with (merge .Values.global.labels .Values.brainstore.writer.labels) }} + {{- with (merge (deepCopy .Values.global.labels) .Values.brainstore.writer.labels) }} labels: {{- toYaml . | nindent 4 }} {{- end }} @@ -31,7 +31,7 @@ spec: {{- if eq .Values.cloud "google" }} gke-workload-identity/use: "true" {{- end }} - {{- with (merge .Values.global.labels .Values.brainstore.writer.labels) }} + {{- with (merge (deepCopy .Values.global.labels) .Values.brainstore.writer.labels) }} {{- toYaml . | nindent 8 }} {{- end }} annotations: diff --git a/braintrust/templates/brainstore-writer-service.yaml b/braintrust/templates/brainstore-writer-service.yaml index 708cda0..e109f14 100644 --- a/braintrust/templates/brainstore-writer-service.yaml +++ b/braintrust/templates/brainstore-writer-service.yaml @@ -3,7 +3,7 @@ kind: Service metadata: name: {{ .Values.brainstore.writer.service.name | default .Values.brainstore.writer.name }} namespace: {{ include "braintrust.namespace" . }} - {{- with (merge .Values.global.labels .Values.brainstore.writer.labels) }} + {{- with (merge (deepCopy .Values.global.labels) .Values.brainstore.writer.labels) }} labels: {{- toYaml . | nindent 4 }} {{- end }} diff --git a/braintrust/tests/labels-merge-configmaps_test.yaml b/braintrust/tests/labels-merge-configmaps_test.yaml new file mode 100644 index 0000000..e64c7d7 --- /dev/null +++ b/braintrust/tests/labels-merge-configmaps_test.yaml @@ -0,0 +1,77 @@ +suite: test label merge isolation for configmaps (deepCopy fix) +templates: + - api-configmap.yaml + - brainstore-reader-configmap.yaml + - brainstore-writer-configmap.yaml +tests: + # These tests verify the deepCopy fix for configmaps. + # Without deepCopy, labels would bleed between components due to merge mutation. + + - it: should give API configmap only global and api-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" + release: + namespace: "braintrust" + template: api-configmap.yaml + asserts: + - equal: + path: metadata.labels.shared + value: global-cm + - equal: + path: metadata.labels["api-specific"] + value: api-cm + - isNull: + path: metadata.labels["reader-specific"] + - isNull: + path: metadata.labels["writer-specific"] + + - it: should give Reader configmap only global and reader-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" + release: + namespace: "braintrust" + template: brainstore-reader-configmap.yaml + asserts: + - equal: + path: metadata.labels.shared + value: global-cm + - equal: + path: metadata.labels["reader-specific"] + value: reader-cm + - isNull: + path: metadata.labels["api-specific"] + - isNull: + path: metadata.labels["writer-specific"] + + - it: should give Writer configmap only global and writer-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" + release: + namespace: "braintrust" + template: brainstore-writer-configmap.yaml + asserts: + - equal: + path: metadata.labels.shared + value: global-cm + - equal: + path: metadata.labels["writer-specific"] + value: writer-cm + - isNull: + path: metadata.labels["api-specific"] + - isNull: + path: metadata.labels["reader-specific"] diff --git a/braintrust/tests/labels-merge-isolation_test.yaml b/braintrust/tests/labels-merge-isolation_test.yaml new file mode 100644 index 0000000..d37dce4 --- /dev/null +++ b/braintrust/tests/labels-merge-isolation_test.yaml @@ -0,0 +1,271 @@ +suite: test label merge isolation (deepCopy fix) +templates: + - api-deployment.yaml + - brainstore-reader-deployment.yaml + - brainstore-writer-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 + # are different, the last rendered component's labels would bleed into + # earlier components through the mutated global.labels. + # + # The fix uses deepCopy to prevent mutation: + # merge (deepCopy .Values.global.labels) .Values.api.labels + + - it: should give API deployment only global and api-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" + release: + namespace: "braintrust" + template: api-deployment.yaml + asserts: + # API deployment should have global labels + - equal: + path: metadata.labels.shared + value: global-value + - equal: + path: metadata.labels.environment + value: production + # API deployment should have api-specific labels + - equal: + path: metadata.labels.component + value: api + - equal: + path: metadata.labels["api-specific"] + value: api-only + # API deployment should NOT have reader or writer specific labels + - 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["api-specific"] + value: api-only + - isNull: + path: spec.template.metadata.labels["reader-specific"] + - isNull: + path: spec.template.metadata.labels["writer-specific"] + + - it: should give Reader deployment only global and reader-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" + release: + namespace: "braintrust" + template: brainstore-reader-deployment.yaml + asserts: + # Reader deployment should have global labels + - equal: + path: metadata.labels.shared + value: global-value + - equal: + path: metadata.labels.environment + value: production + # Reader deployment should have reader-specific labels + - equal: + path: metadata.labels.component + value: reader + - equal: + path: metadata.labels["reader-specific"] + value: reader-only + # Reader deployment should NOT have api or writer specific labels + - isNull: + path: metadata.labels["api-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["reader-specific"] + value: reader-only + - isNull: + path: spec.template.metadata.labels["api-specific"] + - isNull: + path: spec.template.metadata.labels["writer-specific"] + + - it: should give Writer deployment only global and writer-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" + release: + namespace: "braintrust" + template: brainstore-writer-deployment.yaml + asserts: + # Writer deployment should have global labels + - equal: + path: metadata.labels.shared + value: global-value + - equal: + path: metadata.labels.environment + value: production + # Writer deployment should have writer-specific labels + - equal: + path: metadata.labels.component + value: writer + - equal: + path: metadata.labels["writer-specific"] + value: writer-only + # Writer deployment should NOT have api or reader specific labels + - isNull: + path: metadata.labels["api-specific"] + - isNull: + path: metadata.labels["reader-specific"] + # Pod template labels should also be isolated + - equal: + path: spec.template.metadata.labels.shared + value: global-value + - equal: + path: spec.template.metadata.labels["writer-specific"] + value: writer-only + - isNull: + path: spec.template.metadata.labels["api-specific"] + - isNull: + path: spec.template.metadata.labels["reader-specific"] + + - it: should propagate global labels to API without component labels + values: + - __fixtures__/base-values.yaml + set: + global.labels.environment: "staging" + global.labels.team: "platform" + release: + namespace: "braintrust" + template: api-deployment.yaml + asserts: + - equal: + path: metadata.labels.environment + value: staging + - equal: + path: metadata.labels.team + value: platform + + - it: should propagate global labels to Reader without component labels + values: + - __fixtures__/base-values.yaml + set: + global.labels.environment: "staging" + global.labels.team: "platform" + release: + namespace: "braintrust" + template: brainstore-reader-deployment.yaml + asserts: + - equal: + path: metadata.labels.environment + value: staging + - equal: + path: metadata.labels.team + value: platform + + - it: should propagate global labels to Writer without component labels + values: + - __fixtures__/base-values.yaml + set: + global.labels.environment: "staging" + global.labels.team: "platform" + release: + namespace: "braintrust" + template: brainstore-writer-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.global.labels) .Values.component.labels`, + # global labels take precedence over component labels when keys conflict. + # Component labels are only added if the key doesn't exist in global labels. + + - it: should give global labels precedence over API labels when keys conflict + values: + - __fixtures__/base-values.yaml + set: + global.labels.tier: "global-tier" + api.labels.tier: "api-tier" + api.labels.api-only: "api-value" + release: + namespace: "braintrust" + template: api-deployment.yaml + asserts: + # Global label takes precedence for conflicting key + - equal: + path: metadata.labels.tier + value: global-tier + # Non-conflicting component label is still added + - equal: + path: metadata.labels["api-only"] + value: api-value + + - it: should give global labels precedence over Reader labels when keys conflict + values: + - __fixtures__/base-values.yaml + set: + global.labels.tier: "global-tier" + brainstore.reader.labels.tier: "reader-tier" + brainstore.reader.labels.reader-only: "reader-value" + release: + namespace: "braintrust" + template: brainstore-reader-deployment.yaml + asserts: + # Global label takes precedence for conflicting key + - equal: + path: metadata.labels.tier + value: global-tier + # Non-conflicting component label is still added + - equal: + path: metadata.labels["reader-only"] + value: reader-value + + - it: should give global labels precedence over Writer labels when keys conflict + values: + - __fixtures__/base-values.yaml + set: + global.labels.tier: "global-tier" + brainstore.writer.labels.tier: "writer-tier" + brainstore.writer.labels.writer-only: "writer-value" + release: + namespace: "braintrust" + template: brainstore-writer-deployment.yaml + asserts: + # Global label takes precedence for conflicting key + - equal: + path: metadata.labels.tier + value: global-tier + # Non-conflicting component label is still added + - equal: + path: metadata.labels["writer-only"] + value: writer-value diff --git a/braintrust/tests/labels-merge-serviceaccounts_test.yaml b/braintrust/tests/labels-merge-serviceaccounts_test.yaml new file mode 100644 index 0000000..f26efed --- /dev/null +++ b/braintrust/tests/labels-merge-serviceaccounts_test.yaml @@ -0,0 +1,47 @@ +suite: test label merge isolation for service accounts (deepCopy fix) +templates: + - api-serviceaccount.yaml + - brainstore-serviceaccount.yaml +tests: + # These tests verify the deepCopy fix for service accounts. + # Without deepCopy, labels would bleed between components due to merge mutation. + + - it: should give API service account only global and api-specific labels + values: + - __fixtures__/base-values.yaml + set: + global.labels.shared: "global-sa" + api.labels.api-specific: "api-sa" + brainstore.labels.brainstore-specific: "brainstore-sa" + release: + namespace: "braintrust" + template: api-serviceaccount.yaml + asserts: + - equal: + path: metadata.labels.shared + value: global-sa + - equal: + path: metadata.labels["api-specific"] + value: api-sa + - isNull: + path: metadata.labels["brainstore-specific"] + + - it: should give Brainstore service account only global and brainstore-specific labels + values: + - __fixtures__/base-values.yaml + set: + global.labels.shared: "global-sa" + api.labels.api-specific: "api-sa" + brainstore.labels.brainstore-specific: "brainstore-sa" + release: + namespace: "braintrust" + template: brainstore-serviceaccount.yaml + asserts: + - equal: + path: metadata.labels.shared + value: global-sa + - equal: + path: metadata.labels["brainstore-specific"] + value: brainstore-sa + - isNull: + path: metadata.labels["api-specific"] diff --git a/braintrust/tests/labels-merge-services_test.yaml b/braintrust/tests/labels-merge-services_test.yaml new file mode 100644 index 0000000..0f47831 --- /dev/null +++ b/braintrust/tests/labels-merge-services_test.yaml @@ -0,0 +1,84 @@ +suite: test label merge isolation for services (deepCopy fix) +templates: + - api-service.yaml + - brainstore-reader-service.yaml + - brainstore-writer-service.yaml +tests: + # These tests verify the deepCopy fix for services. + # Without deepCopy, labels would bleed between components due to merge mutation. + + - it: should give API service only global and api-specific labels + values: + - __fixtures__/base-values.yaml + set: + global.labels.shared: "global-service" + global.labels.environment: "production" + api.labels.component: "api" + api.labels.api-specific: "api-svc" + brainstore.reader.labels.component: "reader" + brainstore.reader.labels.reader-specific: "reader-svc" + brainstore.writer.labels.component: "writer" + brainstore.writer.labels.writer-specific: "writer-svc" + release: + namespace: "braintrust" + template: api-service.yaml + asserts: + - equal: + path: metadata.labels.shared + value: global-service + - equal: + path: metadata.labels.component + value: api + - equal: + path: metadata.labels["api-specific"] + value: api-svc + - isNull: + path: metadata.labels["reader-specific"] + - isNull: + path: metadata.labels["writer-specific"] + + - it: should give Reader service only global and reader-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" + release: + namespace: "braintrust" + template: brainstore-reader-service.yaml + asserts: + - equal: + path: metadata.labels.shared + value: global-service + - equal: + path: metadata.labels["reader-specific"] + value: reader-svc + - isNull: + path: metadata.labels["api-specific"] + - isNull: + path: metadata.labels["writer-specific"] + + - it: should give Writer service only global and writer-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" + release: + namespace: "braintrust" + template: brainstore-writer-service.yaml + asserts: + - equal: + path: metadata.labels.shared + value: global-service + - equal: + path: metadata.labels["writer-specific"] + value: writer-svc + - isNull: + path: metadata.labels["api-specific"] + - isNull: + path: metadata.labels["reader-specific"]