diff --git a/braintrust/templates/virtualservice.yaml b/braintrust/templates/virtualservice.yaml new file mode 100644 index 0000000..9b05ee4 --- /dev/null +++ b/braintrust/templates/virtualservice.yaml @@ -0,0 +1,28 @@ +{{- if .Values.virtualService.enabled }} +apiVersion: networking.istio.io/v1 +kind: VirtualService +metadata: + name: {{ .Values.virtualService.name | default (printf "%s-vs" .Values.api.name) }} + namespace: {{ include "braintrust.namespace" . }} + {{- with (merge .Values.global.labels .Values.virtualService.labels) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.virtualService.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with .Values.virtualService.gateways }} + gateways: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.virtualService.hosts }} + hosts: + {{- toYaml . | nindent 4 }} + {{- end }} + http: + {{- range .Values.virtualService.http }} + - {{- toYaml . | nindent 6 }} + {{- end }} +{{- end }} diff --git a/braintrust/tests/virtualservice_test.yaml b/braintrust/tests/virtualservice_test.yaml new file mode 100644 index 0000000..cb38a25 --- /dev/null +++ b/braintrust/tests/virtualservice_test.yaml @@ -0,0 +1,252 @@ +suite: test VirtualService template +templates: + - virtualservice.yaml +tests: + - it: should not render when virtualService is disabled + values: + - __fixtures__/base-values.yaml + set: + virtualService.enabled: false + asserts: + - hasDocuments: + count: 0 + + - it: should render VirtualService when enabled + values: + - __fixtures__/base-values.yaml + set: + virtualService.enabled: true + asserts: + - hasDocuments: + count: 1 + - isKind: + of: VirtualService + - isAPIVersion: + of: networking.istio.io/v1 + + - it: should use default name when not provided + values: + - __fixtures__/base-values.yaml + set: + virtualService.enabled: true + api.name: "braintrust-api" + asserts: + - equal: + path: metadata.name + value: braintrust-api-vs + + - it: should use custom name when provided + values: + - __fixtures__/base-values.yaml + set: + virtualService.enabled: true + virtualService.name: "custom-virtualservice" + asserts: + - equal: + path: metadata.name + value: custom-virtualservice + + - it: should use correct namespace from helper when createNamespace is false + values: + - __fixtures__/base-values.yaml + set: + virtualService.enabled: true + global.namespace: "custom-ns" + global.createNamespace: false + release: + namespace: "release-namespace" + asserts: + - equal: + path: metadata.namespace + value: release-namespace + + - it: should use global namespace when createNamespace is true + values: + - __fixtures__/base-values.yaml + set: + virtualService.enabled: true + global.namespace: "custom-ns" + global.createNamespace: true + release: + namespace: "release-namespace" + asserts: + - equal: + path: metadata.namespace + value: custom-ns + + - it: should merge global and virtualService labels + values: + - __fixtures__/base-values.yaml + set: + virtualService.enabled: true + global.labels.environment: "production" + global.labels.team: "platform" + virtualService.labels.component: "virtualservice" + asserts: + - equal: + path: metadata.labels.environment + value: production + - equal: + path: metadata.labels.team + value: platform + - equal: + path: metadata.labels.component + value: virtualservice + + - it: should not bleed labels from other components + values: + - __fixtures__/base-values.yaml + set: + virtualService.enabled: true + global.labels.shared: "global-label" + virtualService.labels.vs-specific: "vs-label" + api.labels.api-specific: "api-label" + brainstore.reader.labels.reader-specific: "reader-label" + asserts: + - equal: + path: metadata.labels.shared + value: global-label + - equal: + path: metadata.labels.vs-specific + value: vs-label + - isNull: + path: metadata.labels.api-specific + - isNull: + path: metadata.labels.reader-specific + + - it: should include annotations when provided + values: + - __fixtures__/base-values.yaml + set: + virtualService.enabled: true + virtualService.annotations: + custom-annotation: "value" + another-annotation: "another-value" + asserts: + - equal: + path: metadata.annotations["custom-annotation"] + value: value + - equal: + path: metadata.annotations["another-annotation"] + value: another-value + + - it: should configure gateways correctly + values: + - __fixtures__/base-values.yaml + set: + virtualService.enabled: true + virtualService.gateways: + - "istio-gateway" + - "istio-ingress/asm-gateway" + asserts: + - equal: + path: spec.gateways[0] + value: istio-gateway + - equal: + path: spec.gateways[1] + value: istio-ingress/asm-gateway + + - it: should configure hosts correctly + values: + - __fixtures__/base-values.yaml + set: + virtualService.enabled: true + virtualService.hosts: + - "braintrust.example.com" + - "api.braintrust.com" + asserts: + - equal: + path: spec.hosts[0] + value: braintrust.example.com + - equal: + path: spec.hosts[1] + value: api.braintrust.com + + - it: should configure HTTP routes with match and destination + values: + - __fixtures__/base-values.yaml + set: + virtualService.enabled: true + virtualService.http: + - match: + - uri: + prefix: "/" + route: + - destination: + host: "braintrust-api" + port: + number: 8000 + asserts: + - equal: + path: spec.http[0].match[0].uri.prefix + value: / + - equal: + path: spec.http[0].route[0].destination.host + value: braintrust-api + - equal: + path: spec.http[0].route[0].destination.port.number + value: 8000 + + - it: should support multiple HTTP routes + values: + - __fixtures__/base-values.yaml + set: + virtualService.enabled: true + virtualService.http: + - match: + - uri: + prefix: "/api" + route: + - destination: + host: "braintrust-api" + port: + number: 8000 + - match: + - uri: + prefix: "/health" + route: + - destination: + host: "braintrust-api" + port: + number: 8000 + asserts: + - equal: + path: spec.http[0].match[0].uri.prefix + value: /api + - equal: + path: spec.http[1].match[0].uri.prefix + value: /health + + - it: should support complex HTTP routing with headers and methods + values: + - __fixtures__/base-values.yaml + set: + virtualService.enabled: true + virtualService.http: + - match: + - uri: + prefix: "/api" + headers: + api-version: + exact: "v2" + method: + exact: "GET" + route: + - destination: + host: "braintrust-api" + port: + number: 8000 + weight: 100 + asserts: + - equal: + path: spec.http[0].match[0].uri.prefix + value: /api + - equal: + path: spec.http[0].match[0].headers.api-version.exact + value: v2 + - equal: + path: spec.http[0].match[0].method.exact + value: GET + - equal: + path: spec.http[0].route[0].weight + value: 100 diff --git a/braintrust/values.yaml b/braintrust/values.yaml index 1328cbc..559a754 100644 --- a/braintrust/values.yaml +++ b/braintrust/values.yaml @@ -285,3 +285,29 @@ azureKeyVaultDriver: - keyVaultSecretName: "azure-storage-connection-string" keyVaultSecretType: "secret" kubernetesSecretKey: "AZURE_STORAGE_CONNECTION_STRING" + +# Istio VirtualService configuration (optional) +# Enable this to create an Istio VirtualService for the API +virtualService: + enabled: false + name: "" # If empty, defaults to "-vs" + labels: {} + annotations: {} + # List of gateway names (can include namespace, e.g., "istio-ingress/asm-gateway") + gateways: + - "istio-gateway" + # List of hosts/domains + hosts: + - "braintrust.example.com" + # HTTP routing rules + # The destination host will use api.service.name (or api.name if service.name is empty) + # The destination port will use api.service.port (default: 8000) + http: + - match: + - uri: + prefix: "/" + route: + - 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