From 1265e9e5c421866bd2b479152cd54108cbc74eec Mon Sep 17 00:00:00 2001 From: kacey Date: Thu, 2 Apr 2026 19:07:04 -0700 Subject: [PATCH 01/23] feat: enable extraContainers --- helm/fiftyone-teams-app/templates/api-deployment.yaml | 3 +++ helm/fiftyone-teams-app/templates/app-deployment.yaml | 3 +++ .../templates/delegated-operator-instance-deployment.yaml | 3 +++ helm/fiftyone-teams-app/templates/plugins-deployment.yaml | 3 +++ 4 files changed, 12 insertions(+) diff --git a/helm/fiftyone-teams-app/templates/api-deployment.yaml b/helm/fiftyone-teams-app/templates/api-deployment.yaml index a0623130d..2db321500 100644 --- a/helm/fiftyone-teams-app/templates/api-deployment.yaml +++ b/helm/fiftyone-teams-app/templates/api-deployment.yaml @@ -89,6 +89,9 @@ spec: {{- with .Values.apiSettings.volumeMounts }} {{- toYaml . | nindent 12 }} {{- end }} + {{- with .Values.apiSettings.extraContainers }} + {{- toYaml . | nindent 8 }} + {{- end }} {{- if .Values.apiSettings.initContainers.enabled }} initContainers: {{- diff --git a/helm/fiftyone-teams-app/templates/app-deployment.yaml b/helm/fiftyone-teams-app/templates/app-deployment.yaml index 54d3309fe..9c2f776dc 100644 --- a/helm/fiftyone-teams-app/templates/app-deployment.yaml +++ b/helm/fiftyone-teams-app/templates/app-deployment.yaml @@ -73,6 +73,9 @@ spec: volumeMounts: {{- toYaml . | nindent 12 }} {{- end }} + {{- with .Values.appSettings.extraContainers }} + {{- toYaml . | nindent 8 }} + {{- end }} {{- if .Values.appSettings.initContainers.enabled }} initContainers: {{- diff --git a/helm/fiftyone-teams-app/templates/delegated-operator-instance-deployment.yaml b/helm/fiftyone-teams-app/templates/delegated-operator-instance-deployment.yaml index ca93b52ce..67c7da4f6 100644 --- a/helm/fiftyone-teams-app/templates/delegated-operator-instance-deployment.yaml +++ b/helm/fiftyone-teams-app/templates/delegated-operator-instance-deployment.yaml @@ -79,6 +79,9 @@ spec: volumeMounts: {{- toYaml . | nindent 12 }} {{- end }} + {{- with $v.extraContainers | default $baseTpl.extraContainers }} + {{- toYaml . | nindent 8 }} + {{- end }} livenessProbe: exec: command: diff --git a/helm/fiftyone-teams-app/templates/plugins-deployment.yaml b/helm/fiftyone-teams-app/templates/plugins-deployment.yaml index 85ef25c92..ecc0cce72 100644 --- a/helm/fiftyone-teams-app/templates/plugins-deployment.yaml +++ b/helm/fiftyone-teams-app/templates/plugins-deployment.yaml @@ -74,6 +74,9 @@ spec: volumeMounts: {{- toYaml . | nindent 12 }} {{- end }} + {{- with .Values.pluginsSettings.extraContainers }} + {{- toYaml . | nindent 8 }} + {{- end }} {{- if .Values.pluginsSettings.initContainers.enabled }} initContainers: {{- From cb333751d948964265cfd8db74a20388e45dc943 Mon Sep 17 00:00:00 2001 From: kacey Date: Fri, 3 Apr 2026 06:41:39 -0700 Subject: [PATCH 02/23] fix: share process namespace --- helm/fiftyone-teams-app/templates/api-deployment.yaml | 3 +++ helm/fiftyone-teams-app/templates/app-deployment.yaml | 3 +++ .../delegated-operator-instance-deployment.yaml | 9 ++++++--- .../fiftyone-teams-app/templates/plugins-deployment.yaml | 3 +++ 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/helm/fiftyone-teams-app/templates/api-deployment.yaml b/helm/fiftyone-teams-app/templates/api-deployment.yaml index 2db321500..2b0311753 100644 --- a/helm/fiftyone-teams-app/templates/api-deployment.yaml +++ b/helm/fiftyone-teams-app/templates/api-deployment.yaml @@ -44,6 +44,9 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ $serviceAccountName }} + {{- if .Values.apiSettings.extraContainers }} + shareProcessNamespace: true + {{- end }} securityContext: {{- toYaml .Values.apiSettings.podSecurityContext | nindent 8 }} containers: diff --git a/helm/fiftyone-teams-app/templates/app-deployment.yaml b/helm/fiftyone-teams-app/templates/app-deployment.yaml index 9c2f776dc..4f18006d2 100644 --- a/helm/fiftyone-teams-app/templates/app-deployment.yaml +++ b/helm/fiftyone-teams-app/templates/app-deployment.yaml @@ -35,6 +35,9 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ include "fiftyone-teams-app.serviceAccountName" . }} + {{- if .Values.appSettings.extraContainers }} + shareProcessNamespace: true + {{- end }} securityContext: {{- toYaml .Values.appSettings.podSecurityContext | nindent 8 }} containers: diff --git a/helm/fiftyone-teams-app/templates/delegated-operator-instance-deployment.yaml b/helm/fiftyone-teams-app/templates/delegated-operator-instance-deployment.yaml index 67c7da4f6..1765e87c4 100644 --- a/helm/fiftyone-teams-app/templates/delegated-operator-instance-deployment.yaml +++ b/helm/fiftyone-teams-app/templates/delegated-operator-instance-deployment.yaml @@ -50,6 +50,9 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ include "fiftyone-teams-app.serviceAccountName" $ }} + {{- if ($v.extraContainers | default $baseTpl.extraContainers) }} + shareProcessNamespace: true + {{- end }} securityContext: {{- toYaml (merge (dict) ($v.podSecurityContext | default dict) ($baseTpl.podSecurityContext)) | nindent 8 }} containers: @@ -79,9 +82,6 @@ spec: volumeMounts: {{- toYaml . | nindent 12 }} {{- end }} - {{- with $v.extraContainers | default $baseTpl.extraContainers }} - {{- toYaml . | nindent 8 }} - {{- end }} livenessProbe: exec: command: @@ -109,6 +109,9 @@ spec: failureThreshold: {{ ($v.startup).failureThreshold | default $baseTpl.startup.failureThreshold }} periodSeconds: {{ ($v.startup).periodSeconds | default $baseTpl.startup.periodSeconds }} timeoutSeconds: {{ ($v.startup).timeoutSeconds | default $baseTpl.startup.timeoutSeconds }} + {{- with $v.extraContainers | default $baseTpl.extraContainers }} + {{- toYaml . | nindent 8 }} + {{- end }} {{- with (merge (dict) ($v.nodeSelector | default dict) ($baseTpl.nodeSelector)) }} nodeSelector: {{- toYaml . | nindent 8 }} diff --git a/helm/fiftyone-teams-app/templates/plugins-deployment.yaml b/helm/fiftyone-teams-app/templates/plugins-deployment.yaml index ecc0cce72..9c3ca49ba 100644 --- a/helm/fiftyone-teams-app/templates/plugins-deployment.yaml +++ b/helm/fiftyone-teams-app/templates/plugins-deployment.yaml @@ -36,6 +36,9 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ include "fiftyone-teams-app.serviceAccountName" . }} + {{- if .Values.pluginsSettings.extraContainers }} + shareProcessNamespace: true + {{- end }} securityContext: {{- toYaml .Values.pluginsSettings.podSecurityContext | nindent 8 }} containers: From ee8665b15fd8293e6ead8d84c415605f78c79696 Mon Sep 17 00:00:00 2001 From: kacey Date: Fri, 3 Apr 2026 15:11:59 -0700 Subject: [PATCH 03/23] fix: add logs access --- .../templates/api-role.yaml | 3 ++ .../templates/telemetry-rbac.yaml | 36 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 helm/fiftyone-teams-app/templates/telemetry-rbac.yaml diff --git a/helm/fiftyone-teams-app/templates/api-role.yaml b/helm/fiftyone-teams-app/templates/api-role.yaml index def64ff6d..fe5bd00b6 100644 --- a/helm/fiftyone-teams-app/templates/api-role.yaml +++ b/helm/fiftyone-teams-app/templates/api-role.yaml @@ -17,4 +17,7 @@ rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "watch", "delete"] + - apiGroups: [""] + resources: ["pods/log"] + verbs: ["get"] {{- end }} diff --git a/helm/fiftyone-teams-app/templates/telemetry-rbac.yaml b/helm/fiftyone-teams-app/templates/telemetry-rbac.yaml new file mode 100644 index 000000000..71e18f155 --- /dev/null +++ b/helm/fiftyone-teams-app/templates/telemetry-rbac.yaml @@ -0,0 +1,36 @@ +{{- if .Values.telemetry }} +{{- if .Values.telemetry.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "fiftyone-teams-app.fullname" . }}-telemetry-logs + namespace: {{ .Values.namespace.name }} + labels: + {{- include "fiftyone-teams-app.commonLabels" . | nindent 4 }} +rules: + - apiGroups: [""] + resources: ["pods/log"] + verbs: ["get"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "fiftyone-teams-app.fullname" . }}-telemetry-logs + namespace: {{ .Values.namespace.name }} + labels: + {{- include "fiftyone-teams-app.commonLabels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "fiftyone-teams-app.fullname" . }}-telemetry-logs +subjects: + - kind: ServiceAccount + name: {{ include "fiftyone-teams-app.serviceAccountName" . }} + namespace: {{ .Values.namespace.name }} + {{- if .Values.apiSettings.rbac.create }} + - kind: ServiceAccount + name: {{ include "teams-api-rbac.service-account-name" . }} + namespace: {{ .Values.namespace.name }} + {{- end }} +{{- end }} +{{- end }} From 35f552dadc1b8faae5909f20737401c3bb07a5e7 Mon Sep 17 00:00:00 2001 From: kacey Date: Fri, 3 Apr 2026 15:55:45 -0700 Subject: [PATCH 04/23] chore: update tests --- tests/unit/helm/api-role_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/unit/helm/api-role_test.go b/tests/unit/helm/api-role_test.go index 13e1aeb8d..2b9fdc770 100644 --- a/tests/unit/helm/api-role_test.go +++ b/tests/unit/helm/api-role_test.go @@ -305,6 +305,11 @@ func (s *apiRoleTemplateTest) TestRules() { "apiGroups": [""], "resources": ["pods"], "verbs": ["get", "list", "watch", "delete"] + }, + { + "apiGroups": [""], + "resources": ["pods/log"], + "verbs": ["get"] } ]` var expectedRules []rbacv1.PolicyRule From 5550d9e8ebbe003636861e1cf1318276b9babb2e Mon Sep 17 00:00:00 2001 From: kacey Date: Sun, 5 Apr 2026 10:33:00 -0700 Subject: [PATCH 05/23] fix: rm unread rbac file --- .../templates/telemetry-rbac.yaml | 36 ------------------- 1 file changed, 36 deletions(-) delete mode 100644 helm/fiftyone-teams-app/templates/telemetry-rbac.yaml diff --git a/helm/fiftyone-teams-app/templates/telemetry-rbac.yaml b/helm/fiftyone-teams-app/templates/telemetry-rbac.yaml deleted file mode 100644 index 71e18f155..000000000 --- a/helm/fiftyone-teams-app/templates/telemetry-rbac.yaml +++ /dev/null @@ -1,36 +0,0 @@ -{{- if .Values.telemetry }} -{{- if .Values.telemetry.enabled }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: {{ include "fiftyone-teams-app.fullname" . }}-telemetry-logs - namespace: {{ .Values.namespace.name }} - labels: - {{- include "fiftyone-teams-app.commonLabels" . | nindent 4 }} -rules: - - apiGroups: [""] - resources: ["pods/log"] - verbs: ["get"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: {{ include "fiftyone-teams-app.fullname" . }}-telemetry-logs - namespace: {{ .Values.namespace.name }} - labels: - {{- include "fiftyone-teams-app.commonLabels" . | nindent 4 }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: {{ include "fiftyone-teams-app.fullname" . }}-telemetry-logs -subjects: - - kind: ServiceAccount - name: {{ include "fiftyone-teams-app.serviceAccountName" . }} - namespace: {{ .Values.namespace.name }} - {{- if .Values.apiSettings.rbac.create }} - - kind: ServiceAccount - name: {{ include "teams-api-rbac.service-account-name" . }} - namespace: {{ .Values.namespace.name }} - {{- end }} -{{- end }} -{{- end }} From 0a182bf461c311854be2299addfc1a8df6851e11 Mon Sep 17 00:00:00 2001 From: kacey Date: Wed, 22 Apr 2026 19:04:02 -0700 Subject: [PATCH 06/23] feat: add nativeSidecarContainers --- docker/README.md | 12 ++++ docker/internal-auth/env.template | 23 ++++++ docker/legacy-auth/env.template | 23 ++++++ .../templates/api-deployment.yaml | 9 ++- .../templates/app-deployment.yaml | 9 ++- ...elegated-operator-instance-deployment.yaml | 6 +- .../templates/plugins-deployment.yaml | 9 ++- .../docker-compose-internal-auth_test.go | 70 +++++++++++++++++++ .../docker-compose-legacy-auth_test.go | 70 +++++++++++++++++++ 9 files changed, 224 insertions(+), 7 deletions(-) diff --git a/docker/README.md b/docker/README.md index 999694101..0fb90ec57 100644 --- a/docker/README.md +++ b/docker/README.md @@ -75,6 +75,7 @@ regarding FiftyOne Enterprise. - [Advanced Configuration](#advanced-configuration) - [Backup And Recovery](#backup-and-recovery) - [Secrets And Sensitive Data](#secrets-and-sensitive-data) + - [Telemetry](#telemetry) - [Snapshot Archival](#snapshot-archival) - [Static Banner Configuration](#static-banner-configuration) - [Storage Credentials and `FIFTYONE_ENCRYPTION_KEY`](#storage-credentials-and-fiftyone_encryption_key) @@ -645,6 +646,17 @@ and [adding secrets](https://docs.voxel51.com/enterprise/secrets.html#adding-secrets) for questions regarding storage and encryption. +### Telemetry + +FiftyOne Enterprise ships an optional telemetry overlay that exposes live +per-service metrics (CPU, memory, FDs, thread counts) and tailed logs on +the Settings → Metrics page. Enable by layering `compose.telemetry.yaml` +onto your existing compose invocation. + +Please refer to the +[telemetry configuration documentation](./docs/configuring-telemetry.md) +for full details. + ### Snapshot Archival Since version v1.5, FiftyOne Enterprise supports diff --git a/docker/internal-auth/env.template b/docker/internal-auth/env.template index 3933cdaf1..395848c92 100644 --- a/docker/internal-auth/env.template +++ b/docker/internal-auth/env.template @@ -98,3 +98,26 @@ FIFTYONE_APP_ENABLE_QUERY_PERFORMANCE=true # This should not exceed the value set in the deployment's license file for max concurrent delegated operators. # Set to 3 by default, meaning delegated operations can be executed without an external executor system. FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS=3 + +# ─── Telemetry (optional; requires compose.telemetry.yaml overlay) ───── +# Redis URL used by the telemetry sidecars and the teams-api / teams-app +# services. Leave blank to use the default in-stack Redis defined by +# compose.telemetry.yaml (redis://telemetry-redis:6379). +# FIFTYONE_TELEMETRY_REDIS_URL= + +# Sidecar image. Default is voxel51/telemetry-sidecar:latest; pin to a +# specific tag in production. +# TELEMETRY_SIDECAR_IMAGE=voxel51/telemetry-sidecar:latest + +# Redis image for the in-stack telemetry backend. +# TELEMETRY_REDIS_IMAGE=redis:7-alpine +# TELEMETRY_REDIS_MAXMEMORY=400mb + +# Namespace label applied to all telemetry pods registered by sidecars. +# TELEMETRY_NAMESPACE=docker + +# TARGET_NAME overrides — substrings the sidecar matches against cmdlines +# to locate each target process. Defaults work for stock images. +# FIFTYONE_APP_TARGET_NAME=hypercorn +# TEAMS_API_TARGET_NAME=fiftyone.teams.api +# TEAMS_DO_TARGET_NAME=fiftyone delegated diff --git a/docker/legacy-auth/env.template b/docker/legacy-auth/env.template index 42c5cc963..7d8217c03 100644 --- a/docker/legacy-auth/env.template +++ b/docker/legacy-auth/env.template @@ -103,3 +103,26 @@ FIFTYONE_APP_ENABLE_QUERY_PERFORMANCE=true # This should not exceed the value set in the deployment's license file for max concurrent delegated operators. # Set to 3 by default, meaning delegated operations can be executed without an external executor system. FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS=3 + +# ─── Telemetry (optional; requires compose.telemetry.yaml overlay) ───── +# Redis URL used by the telemetry sidecars and the teams-api / teams-app +# services. Leave blank to use the default in-stack Redis defined by +# compose.telemetry.yaml (redis://telemetry-redis:6379). +# FIFTYONE_TELEMETRY_REDIS_URL= + +# Sidecar image. Default is voxel51/telemetry-sidecar:latest; pin to a +# specific tag in production. +# TELEMETRY_SIDECAR_IMAGE=voxel51/telemetry-sidecar:latest + +# Redis image for the in-stack telemetry backend. +# TELEMETRY_REDIS_IMAGE=redis:7-alpine +# TELEMETRY_REDIS_MAXMEMORY=400mb + +# Namespace label applied to all telemetry pods registered by sidecars. +# TELEMETRY_NAMESPACE=docker + +# TARGET_NAME overrides — substrings the sidecar matches against cmdlines +# to locate each target process. Defaults work for stock images. +# FIFTYONE_APP_TARGET_NAME=hypercorn +# TEAMS_API_TARGET_NAME=fiftyone.teams.api +# TEAMS_DO_TARGET_NAME=fiftyone delegated diff --git a/helm/fiftyone-teams-app/templates/api-deployment.yaml b/helm/fiftyone-teams-app/templates/api-deployment.yaml index 2b0311753..002f6f660 100644 --- a/helm/fiftyone-teams-app/templates/api-deployment.yaml +++ b/helm/fiftyone-teams-app/templates/api-deployment.yaml @@ -44,7 +44,7 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ $serviceAccountName }} - {{- if .Values.apiSettings.extraContainers }} + {{- if or .Values.apiSettings.extraContainers .Values.apiSettings.nativeSidecarContainers }} shareProcessNamespace: true {{- end }} securityContext: @@ -95,8 +95,9 @@ spec: {{- with .Values.apiSettings.extraContainers }} {{- toYaml . | nindent 8 }} {{- end }} - {{- if .Values.apiSettings.initContainers.enabled }} + {{- if or .Values.apiSettings.initContainers.enabled .Values.apiSettings.nativeSidecarContainers }} initContainers: + {{- if .Values.apiSettings.initContainers.enabled }} {{- include "fiftyone-teams-app.commonInitContainers" ( @@ -107,6 +108,10 @@ spec: "tag" .Values.apiSettings.initContainers.image.tag ) | nindent 8 }} + {{- end }} + {{- with .Values.apiSettings.nativeSidecarContainers }} + {{- toYaml . | nindent 8 }} + {{- end }} {{- end }} {{- with .Values.apiSettings.nodeSelector }} nodeSelector: diff --git a/helm/fiftyone-teams-app/templates/app-deployment.yaml b/helm/fiftyone-teams-app/templates/app-deployment.yaml index 4f18006d2..91d3f9d0c 100644 --- a/helm/fiftyone-teams-app/templates/app-deployment.yaml +++ b/helm/fiftyone-teams-app/templates/app-deployment.yaml @@ -35,7 +35,7 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ include "fiftyone-teams-app.serviceAccountName" . }} - {{- if .Values.appSettings.extraContainers }} + {{- if or .Values.appSettings.extraContainers .Values.appSettings.nativeSidecarContainers }} shareProcessNamespace: true {{- end }} securityContext: @@ -79,8 +79,9 @@ spec: {{- with .Values.appSettings.extraContainers }} {{- toYaml . | nindent 8 }} {{- end }} - {{- if .Values.appSettings.initContainers.enabled }} + {{- if or .Values.appSettings.initContainers.enabled .Values.appSettings.nativeSidecarContainers }} initContainers: + {{- if .Values.appSettings.initContainers.enabled }} {{- include "fiftyone-teams-app.commonInitContainers" ( @@ -91,6 +92,10 @@ spec: "tag" .Values.appSettings.initContainers.image.tag ) | nindent 8 }} + {{- end }} + {{- with .Values.appSettings.nativeSidecarContainers }} + {{- toYaml . | nindent 8 }} + {{- end }} {{- end }} {{- with .Values.appSettings.nodeSelector }} nodeSelector: diff --git a/helm/fiftyone-teams-app/templates/delegated-operator-instance-deployment.yaml b/helm/fiftyone-teams-app/templates/delegated-operator-instance-deployment.yaml index 1765e87c4..ab196e367 100644 --- a/helm/fiftyone-teams-app/templates/delegated-operator-instance-deployment.yaml +++ b/helm/fiftyone-teams-app/templates/delegated-operator-instance-deployment.yaml @@ -50,7 +50,7 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ include "fiftyone-teams-app.serviceAccountName" $ }} - {{- if ($v.extraContainers | default $baseTpl.extraContainers) }} + {{- if or ($v.extraContainers | default $baseTpl.extraContainers) ($v.nativeSidecarContainers | default $baseTpl.nativeSidecarContainers) }} shareProcessNamespace: true {{- end }} securityContext: @@ -112,6 +112,10 @@ spec: {{- with $v.extraContainers | default $baseTpl.extraContainers }} {{- toYaml . | nindent 8 }} {{- end }} + {{- with $v.nativeSidecarContainers | default $baseTpl.nativeSidecarContainers }} + initContainers: + {{- toYaml . | nindent 8 }} + {{- end }} {{- with (merge (dict) ($v.nodeSelector | default dict) ($baseTpl.nodeSelector)) }} nodeSelector: {{- toYaml . | nindent 8 }} diff --git a/helm/fiftyone-teams-app/templates/plugins-deployment.yaml b/helm/fiftyone-teams-app/templates/plugins-deployment.yaml index 9c3ca49ba..5524ae2ad 100644 --- a/helm/fiftyone-teams-app/templates/plugins-deployment.yaml +++ b/helm/fiftyone-teams-app/templates/plugins-deployment.yaml @@ -36,7 +36,7 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ include "fiftyone-teams-app.serviceAccountName" . }} - {{- if .Values.pluginsSettings.extraContainers }} + {{- if or .Values.pluginsSettings.extraContainers .Values.pluginsSettings.nativeSidecarContainers }} shareProcessNamespace: true {{- end }} securityContext: @@ -80,8 +80,9 @@ spec: {{- with .Values.pluginsSettings.extraContainers }} {{- toYaml . | nindent 8 }} {{- end }} - {{- if .Values.pluginsSettings.initContainers.enabled }} + {{- if or .Values.pluginsSettings.initContainers.enabled .Values.pluginsSettings.nativeSidecarContainers }} initContainers: + {{- if .Values.pluginsSettings.initContainers.enabled }} {{- include "fiftyone-teams-app.commonInitContainers" ( @@ -92,6 +93,10 @@ spec: "tag" .Values.pluginsSettings.initContainers.image.tag ) | nindent 8 }} + {{- end }} + {{- with .Values.pluginsSettings.nativeSidecarContainers }} + {{- toYaml . | nindent 8 }} + {{- end }} {{- end }} {{- with .Values.pluginsSettings.nodeSelector }} nodeSelector: diff --git a/tests/unit/compose/docker-compose-internal-auth_test.go b/tests/unit/compose/docker-compose-internal-auth_test.go index 47a2937de..7b404eab2 100644 --- a/tests/unit/compose/docker-compose-internal-auth_test.go +++ b/tests/unit/compose/docker-compose-internal-auth_test.go @@ -28,6 +28,8 @@ var internalAuthComposeFile = filepath.Join(dockerInternalAuthDir, "compose.yaml var internalAuthComposePluginsFile = filepath.Join(dockerInternalAuthDir, "compose.plugins.yaml") var internalAuthComposeDedicatedPluginsFile = filepath.Join(dockerInternalAuthDir, "compose.dedicated-plugins.yaml") var internalAuthComposeDelegatedOperationsFile = filepath.Join(dockerInternalAuthDir, "compose.delegated-operators.yaml") +var internalAuthComposeTelemetryFile = filepath.Join(dockerInternalAuthDir, "compose.telemetry.yaml") +var internalAuthComposeTelemetryDelegatedOperatorsFile = filepath.Join(dockerInternalAuthDir, "compose.telemetry.delegated-operators.yaml") var internalAuthEnvTemplateFilePath = filepath.Join(dockerInternalAuthDir, "env.template") type commonServicesInternalAuthDockerComposeTest struct { @@ -103,6 +105,41 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServicesNames() { "teams-do", }, }, + { + "composeTelemetry", + []string{internalAuthComposeFile, internalAuthComposeTelemetryFile}, + s.dotEnvFiles, + []string{ + "fiftyone-app", + "fiftyone-app-telemetry", + "teams-api", + "teams-api-telemetry", + "teams-app", + "teams-cas", + "telemetry-redis", + }, + }, + { + "composeTelemetryDelegatedOperators", + []string{ + internalAuthComposeFile, + internalAuthComposeDelegatedOperationsFile, + internalAuthComposeTelemetryFile, + internalAuthComposeTelemetryDelegatedOperatorsFile, + }, + s.dotEnvFiles, + []string{ + "fiftyone-app", + "fiftyone-app-telemetry", + "teams-api", + "teams-api-telemetry", + "teams-app", + "teams-cas", + "teams-do", + "teams-do-telemetry", + "telemetry-redis", + }, + }, } for _, testCase := range testCases { @@ -187,6 +224,39 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceImage() { s.dotEnvFiles, "voxel51/fiftyone-teams-cv-full:v2.17.1", }, + { + "telemetryRedis", + "telemetry-redis", + []string{internalAuthComposeFile, internalAuthComposeTelemetryFile}, + s.dotEnvFiles, + "redis:7-alpine", + }, + { + "telemetrySidecarFiftyoneApp", + "fiftyone-app-telemetry", + []string{internalAuthComposeFile, internalAuthComposeTelemetryFile}, + s.dotEnvFiles, + "voxel51/telemetry-sidecar:latest", + }, + { + "telemetrySidecarTeamsApi", + "teams-api-telemetry", + []string{internalAuthComposeFile, internalAuthComposeTelemetryFile}, + s.dotEnvFiles, + "voxel51/telemetry-sidecar:latest", + }, + { + "telemetrySidecarTeamsDo", + "teams-do-telemetry", + []string{ + internalAuthComposeFile, + internalAuthComposeDelegatedOperationsFile, + internalAuthComposeTelemetryFile, + internalAuthComposeTelemetryDelegatedOperatorsFile, + }, + s.dotEnvFiles, + "voxel51/telemetry-sidecar:latest", + }, } for _, testCase := range testCases { diff --git a/tests/unit/compose/docker-compose-legacy-auth_test.go b/tests/unit/compose/docker-compose-legacy-auth_test.go index 8dd53fadc..39c7c2c8e 100644 --- a/tests/unit/compose/docker-compose-legacy-auth_test.go +++ b/tests/unit/compose/docker-compose-legacy-auth_test.go @@ -28,6 +28,8 @@ var legacyAuthComposeFile = filepath.Join(dockerLegacyAuthDir, "compose.yaml") var legacyAuthComposePluginsFile = filepath.Join(dockerLegacyAuthDir, "compose.plugins.yaml") var legacyAuthComposeDedicatedPluginsFile = filepath.Join(dockerLegacyAuthDir, "compose.dedicated-plugins.yaml") var legacyAuthComposeDelegatedOperationsFile = filepath.Join(dockerLegacyAuthDir, "compose.delegated-operators.yaml") +var legacyAuthComposeTelemetryFile = filepath.Join(dockerLegacyAuthDir, "compose.telemetry.yaml") +var legacyAuthComposeTelemetryDelegatedOperatorsFile = filepath.Join(dockerLegacyAuthDir, "compose.telemetry.delegated-operators.yaml") var legacyAuthEnvTemplateFilePath = filepath.Join(dockerLegacyAuthDir, "env.template") type commonServicesLegacyAuthDockerComposeTest struct { @@ -103,6 +105,41 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServicesNames() { "teams-do", }, }, + { + "composeTelemetry", + []string{legacyAuthComposeFile, legacyAuthComposeTelemetryFile}, + s.dotEnvFiles, + []string{ + "fiftyone-app", + "fiftyone-app-telemetry", + "teams-api", + "teams-api-telemetry", + "teams-app", + "teams-cas", + "telemetry-redis", + }, + }, + { + "composeTelemetryDelegatedOperators", + []string{ + legacyAuthComposeFile, + legacyAuthComposeDelegatedOperationsFile, + legacyAuthComposeTelemetryFile, + legacyAuthComposeTelemetryDelegatedOperatorsFile, + }, + s.dotEnvFiles, + []string{ + "fiftyone-app", + "fiftyone-app-telemetry", + "teams-api", + "teams-api-telemetry", + "teams-app", + "teams-cas", + "teams-do", + "teams-do-telemetry", + "telemetry-redis", + }, + }, } for _, testCase := range testCases { @@ -187,6 +224,39 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceImage() { s.dotEnvFiles, "voxel51/fiftyone-teams-cv-full:v2.17.1", }, + { + "telemetryRedis", + "telemetry-redis", + []string{legacyAuthComposeFile, legacyAuthComposeTelemetryFile}, + s.dotEnvFiles, + "redis:7-alpine", + }, + { + "telemetrySidecarFiftyoneApp", + "fiftyone-app-telemetry", + []string{legacyAuthComposeFile, legacyAuthComposeTelemetryFile}, + s.dotEnvFiles, + "voxel51/telemetry-sidecar:latest", + }, + { + "telemetrySidecarTeamsApi", + "teams-api-telemetry", + []string{legacyAuthComposeFile, legacyAuthComposeTelemetryFile}, + s.dotEnvFiles, + "voxel51/telemetry-sidecar:latest", + }, + { + "telemetrySidecarTeamsDo", + "teams-do-telemetry", + []string{ + legacyAuthComposeFile, + legacyAuthComposeDelegatedOperationsFile, + legacyAuthComposeTelemetryFile, + legacyAuthComposeTelemetryDelegatedOperatorsFile, + }, + s.dotEnvFiles, + "voxel51/telemetry-sidecar:latest", + }, } for _, testCase := range testCases { From 88358eff8973e6cfea9156c1540b7889cab5b29a Mon Sep 17 00:00:00 2001 From: kacey Date: Wed, 22 Apr 2026 19:22:40 -0700 Subject: [PATCH 07/23] fix: add missing files --- docker/docs/configuring-telemetry.md | 114 ++++++++++++++++++ ...compose.telemetry.delegated-operators.yaml | 57 +++++++++ docker/internal-auth/compose.telemetry.yaml | 84 +++++++++++++ ...compose.telemetry.delegated-operators.yaml | 57 +++++++++ docker/legacy-auth/compose.telemetry.yaml | 84 +++++++++++++ 5 files changed, 396 insertions(+) create mode 100644 docker/docs/configuring-telemetry.md create mode 100644 docker/internal-auth/compose.telemetry.delegated-operators.yaml create mode 100644 docker/internal-auth/compose.telemetry.yaml create mode 100644 docker/legacy-auth/compose.telemetry.delegated-operators.yaml create mode 100644 docker/legacy-auth/compose.telemetry.yaml diff --git a/docker/docs/configuring-telemetry.md b/docker/docs/configuring-telemetry.md new file mode 100644 index 000000000..93271429d --- /dev/null +++ b/docker/docs/configuring-telemetry.md @@ -0,0 +1,114 @@ + + +
+

+ +Voxel51 Logo   +Voxel51 FiftyOne + +

+
+ + +--- + +# Configuring FiftyOne Enterprise Telemetry + +The telemetry overlay adds a lightweight per-service metrics collector +(sidecar pattern) plus a Redis backend. Once enabled, the Settings → +Metrics page in teams-app displays live CPU / memory / thread / file-descriptor +samples and tailed stdout logs for each observed service. + +This overlay is optional and layered on top of the main compose file. + +## Enable + +Use the `compose.telemetry.yaml` overlay alongside your normal deployment: + +```shell +docker compose \ + -f compose.yaml \ + -f compose.telemetry.yaml \ + up -d +``` + +Can be combined with the plugins or delegated-operators overlays. For +delegated operators, add the companion `compose.telemetry.delegated-operators.yaml` +overlay so the teams-do executor is also observed: + +```shell +docker compose \ + -f compose.yaml \ + -f compose.dedicated-plugins.yaml \ + -f compose.delegated-operators.yaml \ + -f compose.telemetry.yaml \ + -f compose.telemetry.delegated-operators.yaml \ + up -d +``` + +## What it adds + +- `telemetry-redis` — Redis 7 container that holds metric streams and log + entries. Data is capped by a maxmemory policy (`allkeys-lru`) so disk usage + stays bounded. +- `fiftyone-app-telemetry`, `teams-api-telemetry` — sidecar containers, one + per observed service. Each joins the target's PID namespace via + `pid: "service:"` so it can read `/proc//fd/1` and use + psutil to sample CPU, memory, FDs, and thread counts. +- `teams-do-telemetry` (only with `compose.telemetry.delegated-operators.yaml`) — + sidecar in `EXECUTOR_SIDECAR=true` mode that watches the executor for + per-operation child processes and records per-op metrics back to the + `delegated_ops` MongoDB document. +- `FIFTYONE_TELEMETRY_REDIS_URL` injected on `fiftyone-app`, `teams-api`, + `teams-app`, and (when the DO overlay is used) `teams-do` so the in-app + telemetry blueprint and SSE endpoints can read from Redis. + +### Scaling teams-do with telemetry + +docker-compose's `pid: "service:"` only joins a single replica's +PID namespace. To keep the sidecar observation honest, the telemetry DO +overlay **forces `teams-do` replicas to 1**, overriding +`FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS`. + +If you need more than one delegated-operator worker observed at the same +time, either: + +1. Define additional explicit services in a compose override — e.g. + `teams-do-1`, `teams-do-2` — each with a paired `teams-do-N-telemetry` + sidecar using `pid: "service:teams-do-N"`. +2. Deploy via the helm chart, which automatically adds a telemetry sidecar + to every pod in the delegated-operator deployment. + +## Environment overrides + +All knobs live in your `.env` — see `env.template` for the full list: + +| Variable | Default | Purpose | +| ------------------------------ | ----------------------------------- | ---------------------------------------------------- | +| `FIFTYONE_TELEMETRY_REDIS_URL` | `redis://telemetry-redis:6379` | Override to point at an external Redis if desired | +| `TELEMETRY_SIDECAR_IMAGE` | `voxel51/telemetry-sidecar:latest` | Pin sidecar to a specific tag for production | +| `TELEMETRY_REDIS_IMAGE` | `redis:7-alpine` | Alternate redis image | +| `TELEMETRY_REDIS_MAXMEMORY` | `400mb` | Redis maxmemory budget | +| `TELEMETRY_NAMESPACE` | `docker` | Namespace label attached to each registered pod | +| `FIFTYONE_APP_TARGET_NAME` | `hypercorn` | Substring used to locate the fiftyone-app process | +| `TEAMS_API_TARGET_NAME` | `fiftyone.teams.api` | Substring used to locate the teams-api process | +| `TEAMS_DO_TARGET_NAME` | `fiftyone delegated` | Substring used to locate the teams-do process | + +## Access control + +The telemetry endpoints (`/telemetry/*` on teams-api; `/api/telemetry/stream` +and `/api/telemetry/logs` on teams-app) require an authenticated user with +the `ADMIN` role. Non-admin users and unauthenticated requests receive 401/403. + +## Verify + +```shell +docker compose exec telemetry-redis redis-cli HGETALL active_pods +docker compose exec telemetry-redis redis-cli XLEN metrics:docker:fiftyone-app +``` + +`XLEN` should increase over time. If it does not, check the sidecar logs: + +```shell +docker compose logs fiftyone-app-telemetry teams-api-telemetry --tail 20 +``` diff --git a/docker/internal-auth/compose.telemetry.delegated-operators.yaml b/docker/internal-auth/compose.telemetry.delegated-operators.yaml new file mode 100644 index 000000000..0a42f4c7c --- /dev/null +++ b/docker/internal-auth/compose.telemetry.delegated-operators.yaml @@ -0,0 +1,57 @@ +--- +# Telemetry overlay for the delegated operators (teams-do) service. +# Use alongside compose.telemetry.yaml and compose.delegated-operators.yaml: +# +# docker compose -f compose.yaml \ +# -f compose.delegated-operators.yaml \ +# -f compose.telemetry.yaml \ +# -f compose.telemetry.delegated-operators.yaml \ +# up -d +# +# The EXECUTOR_SIDECAR mode watches the teams-do executor for per-operation +# child processes and records per-op metrics back to the delegated_ops +# MongoDB document (via FIFTYONE_DATABASE_URI). +# +# This overlay FORCES `teams-do` replicas to 1 so the sidecar has a +# deterministic 1:1 pairing via the shared PID namespace. docker-compose's +# `pid: "service:"` only joins a single replica's PID namespace, +# so running multiple replicas with a single sidecar would silently miss +# the other replicas. If you need multiple DO workers observed, run one +# teams-do + sidecar pair per worker by defining additional services +# (teams-do-1, teams-do-2, ...), or switch to the helm chart which scales +# the sidecar alongside each pod automatically. +# +# For Proxy Server instructions please see +# https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies +services: + teams-do: + environment: + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + TELEMETRY_SOCKET: /tmp/telemetry/agent.sock + deploy: + replicas: 1 + volumes: + - telemetry-socket:/tmp/telemetry + + teams-do-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:teams-do" + environment: + POD_NAME: teams-do + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: delegated-operator + TARGET_NAME: ${TEAMS_DO_TARGET_NAME:-fiftyone delegated} + EXECUTOR_SIDECAR: "true" + TELEMETRY_SOCKET: /tmp/telemetry/agent.sock + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + FIFTYONE_DATABASE_URI: ${FIFTYONE_DATABASE_URI} + FIFTYONE_DATABASE_NAME: ${FIFTYONE_DATABASE_NAME} + volumes: + - telemetry-socket:/tmp/telemetry + depends_on: + - teams-do + - telemetry-redis + restart: always + +volumes: + telemetry-socket: diff --git a/docker/internal-auth/compose.telemetry.yaml b/docker/internal-auth/compose.telemetry.yaml new file mode 100644 index 000000000..e21212548 --- /dev/null +++ b/docker/internal-auth/compose.telemetry.yaml @@ -0,0 +1,84 @@ +--- +# Telemetry overlay. Adds a Redis backend + sidecar containers that observe +# each target service via a shared PID namespace, publishing metrics and +# log events to Redis. +# +# Layered on top of the main compose file, e.g.: +# docker compose -f compose.yaml -f compose.telemetry.yaml up +# +# Or with delegated operators and/or dedicated plugins: +# docker compose -f compose.yaml \ +# -f compose.delegated-operators.yaml \ +# -f compose.dedicated-plugins.yaml \ +# -f compose.telemetry.yaml up +# +# For Proxy Server instructions please see +# https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies +services: + # Redis backend for telemetry samples and log streams. + telemetry-redis: + image: ${TELEMETRY_REDIS_IMAGE:-redis:7-alpine} + command: + - redis-server + - --save + - "60" + - "1" + - --dir + - /data + - --maxmemory + - ${TELEMETRY_REDIS_MAXMEMORY:-400mb} + - --maxmemory-policy + - allkeys-lru + restart: always + volumes: + - telemetry-redis-data:/data + + # Inject the Redis URL onto the services that read/write telemetry. + # teams-api serves the /telemetry Sanic blueprint; teams-app serves + # /api/telemetry/stream + /api/telemetry/logs SSE endpoints. + fiftyone-app: + environment: + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + + teams-api: + environment: + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + + teams-app: + environment: + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + + # One sidecar per observed service. `pid: "service:"` joins the + # target's PID namespace so the sidecar can read /proc//fd/1 and + # psutil can see the target's process. + + fiftyone-app-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:fiftyone-app" + environment: + POD_NAME: fiftyone-app + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: fiftyone-app + TARGET_NAME: ${FIFTYONE_APP_TARGET_NAME:-hypercorn} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + depends_on: + - fiftyone-app + - telemetry-redis + restart: always + + teams-api-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:teams-api" + environment: + POD_NAME: teams-api + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: teams-api + TARGET_NAME: ${TEAMS_API_TARGET_NAME:-fiftyone.teams.api} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + depends_on: + - teams-api + - telemetry-redis + restart: always + +volumes: + telemetry-redis-data: diff --git a/docker/legacy-auth/compose.telemetry.delegated-operators.yaml b/docker/legacy-auth/compose.telemetry.delegated-operators.yaml new file mode 100644 index 000000000..0a42f4c7c --- /dev/null +++ b/docker/legacy-auth/compose.telemetry.delegated-operators.yaml @@ -0,0 +1,57 @@ +--- +# Telemetry overlay for the delegated operators (teams-do) service. +# Use alongside compose.telemetry.yaml and compose.delegated-operators.yaml: +# +# docker compose -f compose.yaml \ +# -f compose.delegated-operators.yaml \ +# -f compose.telemetry.yaml \ +# -f compose.telemetry.delegated-operators.yaml \ +# up -d +# +# The EXECUTOR_SIDECAR mode watches the teams-do executor for per-operation +# child processes and records per-op metrics back to the delegated_ops +# MongoDB document (via FIFTYONE_DATABASE_URI). +# +# This overlay FORCES `teams-do` replicas to 1 so the sidecar has a +# deterministic 1:1 pairing via the shared PID namespace. docker-compose's +# `pid: "service:"` only joins a single replica's PID namespace, +# so running multiple replicas with a single sidecar would silently miss +# the other replicas. If you need multiple DO workers observed, run one +# teams-do + sidecar pair per worker by defining additional services +# (teams-do-1, teams-do-2, ...), or switch to the helm chart which scales +# the sidecar alongside each pod automatically. +# +# For Proxy Server instructions please see +# https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies +services: + teams-do: + environment: + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + TELEMETRY_SOCKET: /tmp/telemetry/agent.sock + deploy: + replicas: 1 + volumes: + - telemetry-socket:/tmp/telemetry + + teams-do-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:teams-do" + environment: + POD_NAME: teams-do + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: delegated-operator + TARGET_NAME: ${TEAMS_DO_TARGET_NAME:-fiftyone delegated} + EXECUTOR_SIDECAR: "true" + TELEMETRY_SOCKET: /tmp/telemetry/agent.sock + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + FIFTYONE_DATABASE_URI: ${FIFTYONE_DATABASE_URI} + FIFTYONE_DATABASE_NAME: ${FIFTYONE_DATABASE_NAME} + volumes: + - telemetry-socket:/tmp/telemetry + depends_on: + - teams-do + - telemetry-redis + restart: always + +volumes: + telemetry-socket: diff --git a/docker/legacy-auth/compose.telemetry.yaml b/docker/legacy-auth/compose.telemetry.yaml new file mode 100644 index 000000000..e21212548 --- /dev/null +++ b/docker/legacy-auth/compose.telemetry.yaml @@ -0,0 +1,84 @@ +--- +# Telemetry overlay. Adds a Redis backend + sidecar containers that observe +# each target service via a shared PID namespace, publishing metrics and +# log events to Redis. +# +# Layered on top of the main compose file, e.g.: +# docker compose -f compose.yaml -f compose.telemetry.yaml up +# +# Or with delegated operators and/or dedicated plugins: +# docker compose -f compose.yaml \ +# -f compose.delegated-operators.yaml \ +# -f compose.dedicated-plugins.yaml \ +# -f compose.telemetry.yaml up +# +# For Proxy Server instructions please see +# https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies +services: + # Redis backend for telemetry samples and log streams. + telemetry-redis: + image: ${TELEMETRY_REDIS_IMAGE:-redis:7-alpine} + command: + - redis-server + - --save + - "60" + - "1" + - --dir + - /data + - --maxmemory + - ${TELEMETRY_REDIS_MAXMEMORY:-400mb} + - --maxmemory-policy + - allkeys-lru + restart: always + volumes: + - telemetry-redis-data:/data + + # Inject the Redis URL onto the services that read/write telemetry. + # teams-api serves the /telemetry Sanic blueprint; teams-app serves + # /api/telemetry/stream + /api/telemetry/logs SSE endpoints. + fiftyone-app: + environment: + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + + teams-api: + environment: + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + + teams-app: + environment: + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + + # One sidecar per observed service. `pid: "service:"` joins the + # target's PID namespace so the sidecar can read /proc//fd/1 and + # psutil can see the target's process. + + fiftyone-app-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:fiftyone-app" + environment: + POD_NAME: fiftyone-app + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: fiftyone-app + TARGET_NAME: ${FIFTYONE_APP_TARGET_NAME:-hypercorn} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + depends_on: + - fiftyone-app + - telemetry-redis + restart: always + + teams-api-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:teams-api" + environment: + POD_NAME: teams-api + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: teams-api + TARGET_NAME: ${TEAMS_API_TARGET_NAME:-fiftyone.teams.api} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + depends_on: + - teams-api + - telemetry-redis + restart: always + +volumes: + telemetry-redis-data: From 0f8408ca461f265b929d325d1e12e73376273184 Mon Sep 17 00:00:00 2001 From: kacey Date: Thu, 14 May 2026 22:22:29 -0700 Subject: [PATCH 08/23] chore: update compose files --- docker/docs/configuring-telemetry.md | 52 +++++- .../compose.delegated-operators.gpu.yaml | 87 ++++++++++ ...compose.telemetry.delegated-operators.yaml | 9 +- .../compose.telemetry.plugins.yaml | 37 +++++ docker/internal-auth/compose.telemetry.yaml | 24 ++- docker/internal-auth/env.template | 3 +- .../compose.delegated-operators.gpu.yaml | 87 ++++++++++ ...compose.telemetry.delegated-operators.yaml | 9 +- .../compose.telemetry.plugins.yaml | 37 +++++ docker/legacy-auth/compose.telemetry.yaml | 24 ++- docker/legacy-auth/env.template | 3 +- .../configuring-kubernetes-orchestrator.md | 155 ++++++++++++++++++ helm/fiftyone-teams-app/README.md | 15 +- helm/fiftyone-teams-app/README.md.gotmpl | 1 + .../delegated-operator-job-configmap.yaml | 12 ++ helm/fiftyone-teams-app/values.schema.json | 90 ++++++++++ helm/fiftyone-teams-app/values.yaml | 45 +++++ .../docker-compose-internal-auth_test.go | 46 ++++++ .../docker-compose-legacy-auth_test.go | 46 ++++++ 19 files changed, 751 insertions(+), 31 deletions(-) create mode 100644 docker/internal-auth/compose.delegated-operators.gpu.yaml create mode 100644 docker/internal-auth/compose.telemetry.plugins.yaml create mode 100644 docker/legacy-auth/compose.delegated-operators.gpu.yaml create mode 100644 docker/legacy-auth/compose.telemetry.plugins.yaml diff --git a/docker/docs/configuring-telemetry.md b/docker/docs/configuring-telemetry.md index 93271429d..664f8211c 100644 --- a/docker/docs/configuring-telemetry.md +++ b/docker/docs/configuring-telemetry.md @@ -32,9 +32,9 @@ docker compose \ up -d ``` -Can be combined with the plugins or delegated-operators overlays. For -delegated operators, add the companion `compose.telemetry.delegated-operators.yaml` -overlay so the teams-do executor is also observed: +Can be combined with the plugins or delegated-operators overlays. Each +optional service has its own companion telemetry overlay so its sidecar +is added only when that service is enabled: ```shell docker compose \ @@ -42,6 +42,21 @@ docker compose \ -f compose.dedicated-plugins.yaml \ -f compose.delegated-operators.yaml \ -f compose.telemetry.yaml \ + -f compose.telemetry.plugins.yaml \ + -f compose.telemetry.delegated-operators.yaml \ + up -d +``` + +For GPU-enabled delegated operators, layer +`compose.delegated-operators.gpu.yaml` (which includes its own bundled +telemetry sidecar) and activate the `gpu` profile: + +```shell +docker compose --profile gpu \ + -f compose.yaml \ + -f compose.delegated-operators.yaml \ + -f compose.delegated-operators.gpu.yaml \ + -f compose.telemetry.yaml \ -f compose.telemetry.delegated-operators.yaml \ up -d ``` @@ -54,11 +69,19 @@ docker compose \ - `fiftyone-app-telemetry`, `teams-api-telemetry` — sidecar containers, one per observed service. Each joins the target's PID namespace via `pid: "service:"` so it can read `/proc//fd/1` and use - psutil to sample CPU, memory, FDs, and thread counts. + psutil to sample CPU, memory, FDs, and thread counts. Sidecars run + with the `SYS_PTRACE` capability so py-spy can attach to the target. +- `teams-plugins-telemetry` (only with `compose.telemetry.plugins.yaml`) — + sidecar for the dedicated `teams-plugins` service. - `teams-do-telemetry` (only with `compose.telemetry.delegated-operators.yaml`) — sidecar in `EXECUTOR_SIDECAR=true` mode that watches the executor for per-operation child processes and records per-op metrics back to the `delegated_ops` MongoDB document. +- `teams-do-gpu` + `teams-do-gpu-telemetry` (only with + `compose.delegated-operators.gpu.yaml` and `--profile gpu`) — a GPU- + enabled delegated-operator worker registered as a distinct + orchestrator (`-n teams-do-gpu`) plus its paired sidecar. Sidecar + reads GPU metrics via NVML and requires its own GPU reservation. - `FIFTYONE_TELEMETRY_REDIS_URL` injected on `fiftyone-app`, `teams-api`, `teams-app`, and (when the DO overlay is used) `teams-do` so the in-app telemetry blueprint and SSE endpoints can read from Redis. @@ -79,6 +102,21 @@ time, either: 2. Deploy via the helm chart, which automatically adds a telemetry sidecar to every pod in the delegated-operator deployment. +### Sidecar lifecycle on workload restart + +Each sidecar joins its workload's PID namespace at container-create +time. If the workload is recreated (force-recreate, image upgrade, +configuration change) the namespace reference goes stale and Docker +cannot re-attach the sidecar — it stays in `Exited (137)` until +manually recreated. + +The overlays mitigate this with `depends_on..restart: true` +(compose v2.17+), which tells compose to recreate the sidecar in +lockstep with the workload. `docker compose version` must report +v2.17 or newer for this to take effect. If the workload itself crash- +loops, the sidecar follows it into the crash loop, matching the +Kubernetes pod-restart behavior. + ## Environment overrides All knobs live in your `.env` — see `env.template` for the full list: @@ -91,8 +129,12 @@ All knobs live in your `.env` — see `env.template` for the full list: | `TELEMETRY_REDIS_MAXMEMORY` | `400mb` | Redis maxmemory budget | | `TELEMETRY_NAMESPACE` | `docker` | Namespace label attached to each registered pod | | `FIFTYONE_APP_TARGET_NAME` | `hypercorn` | Substring used to locate the fiftyone-app process | -| `TEAMS_API_TARGET_NAME` | `fiftyone.teams.api` | Substring used to locate the teams-api process | +| `TEAMS_API_TARGET_NAME` | `fiftyone-teams-api` | Substring used to locate the teams-api process | +| `TEAMS_PLUGINS_TARGET_NAME` | `hypercorn` | Substring used to locate the teams-plugins process | | `TEAMS_DO_TARGET_NAME` | `fiftyone delegated` | Substring used to locate the teams-do process | +| `NVIDIA_GPU_COUNT` | `1` | GPU reservation for the GPU DO worker + sidecar | +| `NVIDIA_VISIBLE_DEVICES` | `all` | Pass-through to teams-do-gpu / sidecar | +| `NVIDIA_DRIVER_CAPABILITIES` | `compute,utility` | Must include `utility` so NVML is available | ## Access control diff --git a/docker/internal-auth/compose.delegated-operators.gpu.yaml b/docker/internal-auth/compose.delegated-operators.gpu.yaml new file mode 100644 index 000000000..6f38f07bb --- /dev/null +++ b/docker/internal-auth/compose.delegated-operators.gpu.yaml @@ -0,0 +1,87 @@ +--- +# GPU-enabled delegated-operator worker + telemetry sidecar. Requires a +# Linux host with the NVIDIA driver, nvidia-container-toolkit, and the +# `nvidia` runtime configured in dockerd. Docker Desktop on macOS / +# Windows does not support GPU passthrough. +# +# Gated behind the `gpu` profile. Activate with: +# +# docker compose --profile gpu \ +# -f compose.yaml \ +# -f compose.delegated-operators.yaml \ +# -f compose.delegated-operators.gpu.yaml \ +# -f compose.telemetry.yaml \ +# -f compose.telemetry.delegated-operators.yaml \ +# up -d +# +# For Proxy Server instructions please see +# https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies +services: + teams-do-gpu: + image: voxel51/fiftyone-teams-cv-full:v2.17.1 + profiles: ["gpu"] + shm_size: "8g" + command: > + /bin/sh -c "fiftyone delegated launch -t remote -m -n teams-do-gpu" + environment: + API_URL: ${API_URL} + FIFTYONE_DATABASE_ADMIN: "false" + FIFTYONE_DATABASE_NAME: ${FIFTYONE_DATABASE_NAME} + FIFTYONE_DATABASE_URI: ${FIFTYONE_DATABASE_URI} + FIFTYONE_ENCRYPTION_KEY: ${FIFTYONE_ENCRYPTION_KEY} + FIFTYONE_INTERNAL_SERVICE: "true" + FIFTYONE_MEDIA_CACHE_SIZE_BYTES: -1 + FIFTYONE_PLUGINS_DIR: /opt/plugins + NVIDIA_VISIBLE_DEVICES: ${NVIDIA_VISIBLE_DEVICES:-all} + NVIDIA_DRIVER_CAPABILITIES: ${NVIDIA_DRIVER_CAPABILITIES:-compute,utility} + deploy: + replicas: 1 + resources: + reservations: + devices: + - driver: nvidia + count: ${NVIDIA_GPU_COUNT:-1} + capabilities: ["gpu"] + volumes: + - plugins-vol:/opt/plugins:ro + - telemetry-socket-gpu:/tmp/telemetry + restart: always + + teams-do-gpu-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:teams-do-gpu" + profiles: ["gpu"] + cap_add: + - SYS_PTRACE + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: ${NVIDIA_GPU_COUNT:-1} + capabilities: ["gpu"] + environment: + NVIDIA_VISIBLE_DEVICES: ${NVIDIA_VISIBLE_DEVICES:-all} + NVIDIA_DRIVER_CAPABILITIES: ${NVIDIA_DRIVER_CAPABILITIES:-compute,utility} + POD_NAME: teams-do-gpu + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: delegated-operator + TARGET_NAME: ${TEAMS_DO_TARGET_NAME:-fiftyone delegated} + EXECUTOR_SIDECAR: "true" + TELEMETRY_SOCKET: /tmp/telemetry/agent.sock + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + FIFTYONE_DATABASE_URI: ${FIFTYONE_DATABASE_URI} + FIFTYONE_DATABASE_NAME: ${FIFTYONE_DATABASE_NAME} + volumes: + - telemetry-socket-gpu:/tmp/telemetry + depends_on: + teams-do-gpu: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + +volumes: + plugins-vol: + telemetry-socket-gpu: diff --git a/docker/internal-auth/compose.telemetry.delegated-operators.yaml b/docker/internal-auth/compose.telemetry.delegated-operators.yaml index 0a42f4c7c..f6a1c7d51 100644 --- a/docker/internal-auth/compose.telemetry.delegated-operators.yaml +++ b/docker/internal-auth/compose.telemetry.delegated-operators.yaml @@ -36,6 +36,8 @@ services: teams-do-telemetry: image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} pid: "service:teams-do" + cap_add: + - SYS_PTRACE environment: POD_NAME: teams-do POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} @@ -49,8 +51,11 @@ services: volumes: - telemetry-socket:/tmp/telemetry depends_on: - - teams-do - - telemetry-redis + teams-do: + condition: service_started + restart: true + telemetry-redis: + condition: service_started restart: always volumes: diff --git a/docker/internal-auth/compose.telemetry.plugins.yaml b/docker/internal-auth/compose.telemetry.plugins.yaml new file mode 100644 index 000000000..ac1de8bf2 --- /dev/null +++ b/docker/internal-auth/compose.telemetry.plugins.yaml @@ -0,0 +1,37 @@ +--- +# Telemetry overlay for the dedicated teams-plugins service. The sidecar +# shares the target's PID namespace via `pid: "service:teams-plugins"`. +# +# Layered after compose.dedicated-plugins.yaml + compose.telemetry.yaml: +# +# docker compose -f compose.yaml \ +# -f compose.dedicated-plugins.yaml \ +# -f compose.telemetry.yaml \ +# -f compose.telemetry.plugins.yaml \ +# up -d +# +# For Proxy Server instructions please see +# https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies +services: + teams-plugins: + environment: + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + + teams-plugins-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:teams-plugins" + cap_add: + - SYS_PTRACE + environment: + POD_NAME: teams-plugins + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: teams-plugins + TARGET_NAME: ${TEAMS_PLUGINS_TARGET_NAME:-hypercorn} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + depends_on: + teams-plugins: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always diff --git a/docker/internal-auth/compose.telemetry.yaml b/docker/internal-auth/compose.telemetry.yaml index e21212548..89b9d57a4 100644 --- a/docker/internal-auth/compose.telemetry.yaml +++ b/docker/internal-auth/compose.telemetry.yaml @@ -15,7 +15,6 @@ # For Proxy Server instructions please see # https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies services: - # Redis backend for telemetry samples and log streams. telemetry-redis: image: ${TELEMETRY_REDIS_IMAGE:-redis:7-alpine} command: @@ -33,9 +32,6 @@ services: volumes: - telemetry-redis-data:/data - # Inject the Redis URL onto the services that read/write telemetry. - # teams-api serves the /telemetry Sanic blueprint; teams-app serves - # /api/telemetry/stream + /api/telemetry/logs SSE endpoints. fiftyone-app: environment: FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} @@ -55,6 +51,8 @@ services: fiftyone-app-telemetry: image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} pid: "service:fiftyone-app" + cap_add: + - SYS_PTRACE environment: POD_NAME: fiftyone-app POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} @@ -62,22 +60,30 @@ services: TARGET_NAME: ${FIFTYONE_APP_TARGET_NAME:-hypercorn} FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} depends_on: - - fiftyone-app - - telemetry-redis + fiftyone-app: + condition: service_started + restart: true + telemetry-redis: + condition: service_started restart: always teams-api-telemetry: image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} pid: "service:teams-api" + cap_add: + - SYS_PTRACE environment: POD_NAME: teams-api POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} SERVICE_TYPE: teams-api - TARGET_NAME: ${TEAMS_API_TARGET_NAME:-fiftyone.teams.api} + TARGET_NAME: ${TEAMS_API_TARGET_NAME:-fiftyone-teams-api} FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} depends_on: - - teams-api - - telemetry-redis + teams-api: + condition: service_started + restart: true + telemetry-redis: + condition: service_started restart: always volumes: diff --git a/docker/internal-auth/env.template b/docker/internal-auth/env.template index 395848c92..270780446 100644 --- a/docker/internal-auth/env.template +++ b/docker/internal-auth/env.template @@ -119,5 +119,6 @@ FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS=3 # TARGET_NAME overrides — substrings the sidecar matches against cmdlines # to locate each target process. Defaults work for stock images. # FIFTYONE_APP_TARGET_NAME=hypercorn -# TEAMS_API_TARGET_NAME=fiftyone.teams.api +# TEAMS_API_TARGET_NAME=fiftyone-teams-api +# TEAMS_PLUGINS_TARGET_NAME=hypercorn # TEAMS_DO_TARGET_NAME=fiftyone delegated diff --git a/docker/legacy-auth/compose.delegated-operators.gpu.yaml b/docker/legacy-auth/compose.delegated-operators.gpu.yaml new file mode 100644 index 000000000..6f38f07bb --- /dev/null +++ b/docker/legacy-auth/compose.delegated-operators.gpu.yaml @@ -0,0 +1,87 @@ +--- +# GPU-enabled delegated-operator worker + telemetry sidecar. Requires a +# Linux host with the NVIDIA driver, nvidia-container-toolkit, and the +# `nvidia` runtime configured in dockerd. Docker Desktop on macOS / +# Windows does not support GPU passthrough. +# +# Gated behind the `gpu` profile. Activate with: +# +# docker compose --profile gpu \ +# -f compose.yaml \ +# -f compose.delegated-operators.yaml \ +# -f compose.delegated-operators.gpu.yaml \ +# -f compose.telemetry.yaml \ +# -f compose.telemetry.delegated-operators.yaml \ +# up -d +# +# For Proxy Server instructions please see +# https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies +services: + teams-do-gpu: + image: voxel51/fiftyone-teams-cv-full:v2.17.1 + profiles: ["gpu"] + shm_size: "8g" + command: > + /bin/sh -c "fiftyone delegated launch -t remote -m -n teams-do-gpu" + environment: + API_URL: ${API_URL} + FIFTYONE_DATABASE_ADMIN: "false" + FIFTYONE_DATABASE_NAME: ${FIFTYONE_DATABASE_NAME} + FIFTYONE_DATABASE_URI: ${FIFTYONE_DATABASE_URI} + FIFTYONE_ENCRYPTION_KEY: ${FIFTYONE_ENCRYPTION_KEY} + FIFTYONE_INTERNAL_SERVICE: "true" + FIFTYONE_MEDIA_CACHE_SIZE_BYTES: -1 + FIFTYONE_PLUGINS_DIR: /opt/plugins + NVIDIA_VISIBLE_DEVICES: ${NVIDIA_VISIBLE_DEVICES:-all} + NVIDIA_DRIVER_CAPABILITIES: ${NVIDIA_DRIVER_CAPABILITIES:-compute,utility} + deploy: + replicas: 1 + resources: + reservations: + devices: + - driver: nvidia + count: ${NVIDIA_GPU_COUNT:-1} + capabilities: ["gpu"] + volumes: + - plugins-vol:/opt/plugins:ro + - telemetry-socket-gpu:/tmp/telemetry + restart: always + + teams-do-gpu-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:teams-do-gpu" + profiles: ["gpu"] + cap_add: + - SYS_PTRACE + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: ${NVIDIA_GPU_COUNT:-1} + capabilities: ["gpu"] + environment: + NVIDIA_VISIBLE_DEVICES: ${NVIDIA_VISIBLE_DEVICES:-all} + NVIDIA_DRIVER_CAPABILITIES: ${NVIDIA_DRIVER_CAPABILITIES:-compute,utility} + POD_NAME: teams-do-gpu + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: delegated-operator + TARGET_NAME: ${TEAMS_DO_TARGET_NAME:-fiftyone delegated} + EXECUTOR_SIDECAR: "true" + TELEMETRY_SOCKET: /tmp/telemetry/agent.sock + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + FIFTYONE_DATABASE_URI: ${FIFTYONE_DATABASE_URI} + FIFTYONE_DATABASE_NAME: ${FIFTYONE_DATABASE_NAME} + volumes: + - telemetry-socket-gpu:/tmp/telemetry + depends_on: + teams-do-gpu: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + +volumes: + plugins-vol: + telemetry-socket-gpu: diff --git a/docker/legacy-auth/compose.telemetry.delegated-operators.yaml b/docker/legacy-auth/compose.telemetry.delegated-operators.yaml index 0a42f4c7c..f6a1c7d51 100644 --- a/docker/legacy-auth/compose.telemetry.delegated-operators.yaml +++ b/docker/legacy-auth/compose.telemetry.delegated-operators.yaml @@ -36,6 +36,8 @@ services: teams-do-telemetry: image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} pid: "service:teams-do" + cap_add: + - SYS_PTRACE environment: POD_NAME: teams-do POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} @@ -49,8 +51,11 @@ services: volumes: - telemetry-socket:/tmp/telemetry depends_on: - - teams-do - - telemetry-redis + teams-do: + condition: service_started + restart: true + telemetry-redis: + condition: service_started restart: always volumes: diff --git a/docker/legacy-auth/compose.telemetry.plugins.yaml b/docker/legacy-auth/compose.telemetry.plugins.yaml new file mode 100644 index 000000000..ac1de8bf2 --- /dev/null +++ b/docker/legacy-auth/compose.telemetry.plugins.yaml @@ -0,0 +1,37 @@ +--- +# Telemetry overlay for the dedicated teams-plugins service. The sidecar +# shares the target's PID namespace via `pid: "service:teams-plugins"`. +# +# Layered after compose.dedicated-plugins.yaml + compose.telemetry.yaml: +# +# docker compose -f compose.yaml \ +# -f compose.dedicated-plugins.yaml \ +# -f compose.telemetry.yaml \ +# -f compose.telemetry.plugins.yaml \ +# up -d +# +# For Proxy Server instructions please see +# https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies +services: + teams-plugins: + environment: + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + + teams-plugins-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:teams-plugins" + cap_add: + - SYS_PTRACE + environment: + POD_NAME: teams-plugins + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: teams-plugins + TARGET_NAME: ${TEAMS_PLUGINS_TARGET_NAME:-hypercorn} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + depends_on: + teams-plugins: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always diff --git a/docker/legacy-auth/compose.telemetry.yaml b/docker/legacy-auth/compose.telemetry.yaml index e21212548..89b9d57a4 100644 --- a/docker/legacy-auth/compose.telemetry.yaml +++ b/docker/legacy-auth/compose.telemetry.yaml @@ -15,7 +15,6 @@ # For Proxy Server instructions please see # https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies services: - # Redis backend for telemetry samples and log streams. telemetry-redis: image: ${TELEMETRY_REDIS_IMAGE:-redis:7-alpine} command: @@ -33,9 +32,6 @@ services: volumes: - telemetry-redis-data:/data - # Inject the Redis URL onto the services that read/write telemetry. - # teams-api serves the /telemetry Sanic blueprint; teams-app serves - # /api/telemetry/stream + /api/telemetry/logs SSE endpoints. fiftyone-app: environment: FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} @@ -55,6 +51,8 @@ services: fiftyone-app-telemetry: image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} pid: "service:fiftyone-app" + cap_add: + - SYS_PTRACE environment: POD_NAME: fiftyone-app POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} @@ -62,22 +60,30 @@ services: TARGET_NAME: ${FIFTYONE_APP_TARGET_NAME:-hypercorn} FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} depends_on: - - fiftyone-app - - telemetry-redis + fiftyone-app: + condition: service_started + restart: true + telemetry-redis: + condition: service_started restart: always teams-api-telemetry: image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} pid: "service:teams-api" + cap_add: + - SYS_PTRACE environment: POD_NAME: teams-api POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} SERVICE_TYPE: teams-api - TARGET_NAME: ${TEAMS_API_TARGET_NAME:-fiftyone.teams.api} + TARGET_NAME: ${TEAMS_API_TARGET_NAME:-fiftyone-teams-api} FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} depends_on: - - teams-api - - telemetry-redis + teams-api: + condition: service_started + restart: true + telemetry-redis: + condition: service_started restart: always volumes: diff --git a/docker/legacy-auth/env.template b/docker/legacy-auth/env.template index 7d8217c03..ef62237a1 100644 --- a/docker/legacy-auth/env.template +++ b/docker/legacy-auth/env.template @@ -124,5 +124,6 @@ FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS=3 # TARGET_NAME overrides — substrings the sidecar matches against cmdlines # to locate each target process. Defaults work for stock images. # FIFTYONE_APP_TARGET_NAME=hypercorn -# TEAMS_API_TARGET_NAME=fiftyone.teams.api +# TEAMS_API_TARGET_NAME=fiftyone-teams-api +# TEAMS_PLUGINS_TARGET_NAME=hypercorn # TEAMS_DO_TARGET_NAME=fiftyone delegated diff --git a/docs/orchestrators/configuring-kubernetes-orchestrator.md b/docs/orchestrators/configuring-kubernetes-orchestrator.md index ce1317c9a..a6f8d6bd8 100644 --- a/docs/orchestrators/configuring-kubernetes-orchestrator.md +++ b/docs/orchestrators/configuring-kubernetes-orchestrator.md @@ -25,6 +25,7 @@ - [Template Storage Options](#template-storage-options) - [Secrets Options](#secrets-options) - [Separate CPU and GPU Templates](#separate-cpu-and-gpu-templates) +- [Optional: Telemetry Sidecar](#optional-telemetry-sidecar) - [Refresh Orchestrator Operators](#refresh-orchestrator-operators) - [Additional Considerations](#additional-considerations) - [Credential Rotation](#credential-rotation) @@ -459,6 +460,99 @@ fom.register_orchestrator( ) ``` +## Optional: Telemetry Sidecar + +If your deployment runs the telemetry overlay (see the helm chart's +`extraContainers` / `nativeSidecarContainers` options or the docker +`compose.telemetry.yaml`), you can attach a per-Job telemetry sidecar to +on-demand Kubernetes orchestrators as well. This emits per-operation +metrics back to the same Redis backend so the Settings → Metrics page +sees individual delegated runs. + +Use a Kubernetes +[native sidecar](https://kubernetes.io/docs/concepts/workloads/pods/sidecar-containers/) +(an `initContainer` with `restartPolicy: Always`, requires Kubernetes +1.29+). A regular +sidecar container would block Job completion — the Job stays in +`Running` until every container exits. Native sidecars are +auto-terminated by the kubelet when all non-sidecar containers +complete, so the Job finalizes cleanly. + +Add the following to your Job template's Pod spec: + +```yaml +spec: + shareProcessNamespace: true + initContainers: + - name: telemetry-sidecar + image: voxel51/telemetry-sidecar:latest + restartPolicy: Always + securityContext: + capabilities: + add: [SYS_PTRACE] + env: + - name: TARGET_NAME + value: delegated + - name: SERVICE_TYPE + value: delegated-operator + - name: EXECUTOR_SIDECAR + value: "true" + - name: TELEMETRY_SOCKET + value: /tmp/telemetry/agent.sock + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: FIFTYONE_TELEMETRY_REDIS_URL + value: redis://telemetry-redis.fiftyone.svc.cluster.local:6379 + - name: FIFTYONE_DATABASE_URI + valueFrom: + secretKeyRef: + name: fiftyone-secrets + key: database-uri + - name: FIFTYONE_DATABASE_NAME + valueFrom: + secretKeyRef: + name: fiftyone-secrets + key: database-name + volumeMounts: + - mountPath: /tmp/telemetry + name: telemetry-socket + containers: + - name: task-worker + # ... existing container config ... + env: + # ... existing env, plus: + - name: TELEMETRY_SOCKET + value: /tmp/telemetry/agent.sock + - name: FIFTYONE_TELEMETRY_REDIS_URL + value: redis://telemetry-redis.fiftyone.svc.cluster.local:6379 + volumeMounts: + # ... existing mounts, plus: + - mountPath: /tmp/telemetry + name: telemetry-socket + volumes: + - name: telemetry-socket + emptyDir: {} +``` + +Notes: + +- `shareProcessNamespace: true` lets the sidecar's psutil call see the + worker process via `/proc/` in the shared PID namespace. +- `SYS_PTRACE` is required so py-spy can attach to the worker. +- `EXECUTOR_SIDECAR=true` switches the sidecar into per-operation mode; + the worker writes execution metadata to `TELEMETRY_SOCKET` and the + sidecar records per-op metrics into the `delegated_ops` MongoDB + document. +- The `FIFTYONE_TELEMETRY_REDIS_URL` must be reachable from wherever + the Job runs. For same-cluster Jobs use the in-cluster service DNS + name. For remote clusters, use a routable hostname or load balancer. + ## Refresh Orchestrator Operators This step is only required if you've added a plugin directory with custom @@ -542,6 +636,53 @@ spec: serviceAccountName: your-org-fiftyone-teams podSecurityContext: runAsNonRoot: false + shareProcessNamespace: true + initContainers: + - name: telemetry-sidecar + image: voxel51/telemetry-sidecar:latest + restartPolicy: Always + securityContext: + capabilities: + add: [SYS_PTRACE] + env: + - name: TARGET_NAME + value: delegated + - name: SERVICE_TYPE + value: delegated-operator + - name: EXECUTOR_SIDECAR + value: "true" + - name: TELEMETRY_SOCKET + value: /tmp/telemetry/agent.sock + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: FIFTYONE_TELEMETRY_REDIS_URL + value: redis://telemetry-redis.your-org-fiftyone-ai.svc.cluster.local:6379 + - name: FIFTYONE_DATABASE_URI + valueFrom: + secretKeyRef: + key: mongodbConnectionString + name: your-org-teams-secrets + - name: FIFTYONE_DATABASE_NAME + valueFrom: + secretKeyRef: + key: fiftyoneDatabaseName + name: your-org-teams-secrets + resources: + limits: + cpu: 50m + memory: 512Mi + requests: + cpu: 50m + memory: 512Mi + volumeMounts: + - mountPath: /tmp/telemetry + name: telemetry-socket containers: - name: task-worker image: registry/image:tag @@ -585,8 +726,12 @@ spec: value: "true" - name: FIFTYONE_PLUGINS_DIR value: /opt/plugins + - name: FIFTYONE_TELEMETRY_REDIS_URL + value: redis://telemetry-redis.your-org-fiftyone-ai.svc.cluster.local:6379 - name: NUMBA_CACHE_DIR value: /tmp/numba + - name: TELEMETRY_SOCKET + value: /tmp/telemetry/agent.sock - name: TORCH_HOME value: /opt/fiftyone_zoo/your-org/torch resources: @@ -618,6 +763,8 @@ spec: name: memory-media-cache-vol - mountPath: /dev/shm name: shm-vol + - mountPath: /tmp/telemetry + name: telemetry-socket volumes: - name: nfs-plugins-ro-vol persistentVolumeClaim: @@ -652,5 +799,13 @@ spec: medium: Memory sizeLimit: 2Gi name: shm-vol + - emptyDir: {} + name: telemetry-socket restartPolicy: Never ``` + +The `telemetry-sidecar` init container above is optional. Remove it +(plus `shareProcessNamespace: true`, the `TELEMETRY_SOCKET` / +`FIFTYONE_TELEMETRY_REDIS_URL` env vars on `task-worker`, and the +`telemetry-socket` volume + mount) if you are not running the +telemetry overlay. diff --git a/helm/fiftyone-teams-app/README.md b/helm/fiftyone-teams-app/README.md index d1cab8523..8460ea7ab 100644 --- a/helm/fiftyone-teams-app/README.md +++ b/helm/fiftyone-teams-app/README.md @@ -726,6 +726,7 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): | apiSettings.env.FIFTYONE_LOGGING_FORMAT | string | `"text"` | The format to use for log messages. Can be one of `json` or `text`. | | apiSettings.env.GRAPHQL_DEFAULT_LIMIT | int | `10` | Default number of returned items when listing in GraphQL queries. Can be overridden in the request. | | apiSettings.env.LOGGING_LEVEL | string | `"INFO"` | Logging level. Overrides the value of `FIFTYONE_ENV`. Can be one of "DEBUG", "INFO", "WARN", "ERROR", or "CRITICAL". | +| apiSettings.extraContainers | list | `[]` | Additional containers to run in the `teams-api` pod alongside the main container. When non-empty, the pod is configured with `shareProcessNamespace: true`. [Reference][sidecar-containers]. | | apiSettings.image.pullPolicy | string | `"Always"` | Instruct when the kubelet should pull (download) the specified image. One of `IfNotPresent`, `Always` or `Never`. [Reference][image-pull-policy]. | | apiSettings.image.repository | string | `"voxel51/fiftyone-teams-api"` | Container image for the `teams-api`. | | apiSettings.image.tag | string | `""` | Image tag for `teams-api`. Defaults to the chart version. | @@ -738,6 +739,7 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): | apiSettings.liveness.failureThreshold | int | `5` | Number of times to retry the liveness probe for the `teams-api`. [Reference][probes]. | | apiSettings.liveness.periodSeconds | int | `15` | How often (in seconds) to perform the liveness probe for `teams-api`. [Reference][probes]. | | apiSettings.liveness.timeoutSeconds | int | `5` | Number of seconds after which the liveness probe times out for the `teams-api`. [Reference][probes]. | +| apiSettings.nativeSidecarContainers | list | `[]` | Native sidecar containers for the `teams-api` pod (Kubernetes 1.29+). Entries are rendered under `initContainers`; set `restartPolicy: Always` on each entry to enable the native sidecar lifecycle. [Reference][sidecar-containers]. | | apiSettings.nodeSelector | object | `{}` | nodeSelector for `teams-api`. [Reference][node-selector]. | | apiSettings.podAnnotations | object | `{}` | Annotations for pods for `teams-api`. [Reference][annotations]. | | apiSettings.podDisruptionBudget | object | `{"enabled":false,"minAvailable":null}` | Pod Disruption Budget for pods for `teams-api`. [Reference][pod-disruption-budget]. | @@ -793,6 +795,7 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): | appSettings.env.FIFTYONE_MEDIA_CACHE_APP_IMAGES | bool | `false` | Controls whether cloud media images will be downloaded and added to the local cache upon viewing media in the app. | | appSettings.env.FIFTYONE_MEDIA_CACHE_SIZE_BYTES | int | `-1` | Set the media cache size (in bytes) for the local FiftyOne App processes. When not set, the app's default value is 32 GiB. `-1` disables the media cache garbage collection (and cache size is unlimited). | | appSettings.env.FIFTYONE_SIGNED_URL_EXPIRATION | int | `24` | Set the time-to-live for signed URLs generated by the application in hours | +| appSettings.extraContainers | list | `[]` | Additional containers to run in the `fiftyone-app` pod alongside the main container. When non-empty, the pod is configured with `shareProcessNamespace: true`. [Reference][sidecar-containers]. | | appSettings.image.pullPolicy | string | `"Always"` | Instruct when the kubelet should pull (download) the specified image. One of `IfNotPresent`, `Always` or `Never`. [Reference][image-pull-policy]. | | appSettings.image.repository | string | `"voxel51/fiftyone-app"` | Container image for `fiftyone-app`. | | appSettings.image.tag | string | `""` | Image tag for `fiftyone-app`. Defaults to the chart version. | @@ -805,6 +808,7 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): | appSettings.liveness.failureThreshold | int | `5` | Number of times to retry the liveness probe for the `fiftyone-app`. [Reference][probes]. | | appSettings.liveness.periodSeconds | int | `15` | How often (in seconds) to perform the liveness probe for `fiftyone-app`. [Reference][probes]. | | appSettings.liveness.timeoutSeconds | int | `5` | Timeout for the liveness probe for the `fiftyone-app`. [Reference][probes]. | +| appSettings.nativeSidecarContainers | list | `[]` | Native sidecar containers for the `fiftyone-app` pod (Kubernetes 1.29+). Entries are rendered under `initContainers`; set `restartPolicy: Always` on each entry to enable the native sidecar lifecycle. [Reference][sidecar-containers]. | | appSettings.nodeSelector | object | `{}` | nodeSelector for `fiftyone-app`. [Reference][node-selector]. | | appSettings.podAnnotations | object | `{}` | Annotations for pods for `fiftyone-app`. [Reference][annotations]. | | appSettings.podDisruptionBudget | object | `{"enabled":false,"minAvailable":null}` | Pod Disruption Budget for pods for `fiftyone-app`. [Reference][pod-disruption-budget]. | @@ -883,13 +887,14 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): | casSettings.volumes | list | `[]` | Volumes for `teams-cas`. [Reference][volumes]. | | delegatedOperatorDeployments.deployments | object | `{"teamsDoCpuDefault":{"enabled":false,"env":{"FIFTYONE_MEDIA_CACHE_DIR":"/opt/media_cache","FIFTYONE_MEDIA_CACHE_SIZE_BYTES":"2147483648"},"replicaCount":1,"resources":{"limits":{"cpu":4,"ephemeral-storage":"1Gi","memory":"16Gi"},"requests":{"cpu":4,"ephemeral-storage":"1Gi","memory":"16Gi"}},"volumeMounts":[{"mountPath":"/dev/shm","name":"shm-vol"},{"mountPath":"/opt/media_cache","name":"memory-media-cache-vol"}],"volumes":[{"emptyDir":{"medium":"Memory","sizeLimit":"2Gi"},"name":"shm-vol"},{"emptyDir":{"medium":"Memory","sizeLimit":"2.5Gi"},"name":"memory-media-cache-vol"}]}}` | Additional deployments to configure. Each template will use .Values.delegatedOperatorDeployments.template as a base. Each template value may be overridden. Maps/dictionaries will be merged key-wise, with the deployment instance taking precedence. List values will not be merged, but be overridden completely by the deployment instance. | | delegatedOperatorDeployments.deployments.teamsDoCpuDefault | object | `{"enabled":false,"env":{"FIFTYONE_MEDIA_CACHE_DIR":"/opt/media_cache","FIFTYONE_MEDIA_CACHE_SIZE_BYTES":"2147483648"},"replicaCount":1,"resources":{"limits":{"cpu":4,"ephemeral-storage":"1Gi","memory":"16Gi"},"requests":{"cpu":4,"ephemeral-storage":"1Gi","memory":"16Gi"}},"volumeMounts":[{"mountPath":"/dev/shm","name":"shm-vol"},{"mountPath":"/opt/media_cache","name":"memory-media-cache-vol"}],"volumes":[{"emptyDir":{"medium":"Memory","sizeLimit":"2Gi"},"name":"shm-vol"},{"emptyDir":{"medium":"Memory","sizeLimit":"2.5Gi"},"name":"memory-media-cache-vol"}]}` | Default (CPU-only) delegated operator runner. Defaults to an 4vCPU, 16Gi RAM runner. The deployment is backed with a 2.5Gi in-memory volume for caching media and an extra 2Gi of shared memory. | -| delegatedOperatorDeployments.template | object | `{"affinity":{},"deploymentAnnotations":{},"description":"","env":{"FIFTYONE_DELEGATED_OPERATION_LOG_PATH":"","FIFTYONE_INTERNAL_SERVICE":true,"FIFTYONE_MEDIA_CACHE_SIZE_BYTES":-1},"image":{"pullPolicy":"Always","repository":"voxel51/fiftyone-teams-cv-full","tag":""},"labels":{},"liveness":{"failureThreshold":5,"periodSeconds":30,"timeoutSeconds":30},"nodeSelector":{},"podAnnotations":{},"podDisruptionBudget":{"enabled":false,"minAvailable":null},"podSecurityContext":{},"readiness":{"failureThreshold":5,"periodSeconds":30,"timeoutSeconds":30},"replicaCount":3,"resources":{"limits":{},"requests":{}},"secretEnv":{},"securityContext":{},"startup":{"failureThreshold":5,"periodSeconds":30,"timeoutSeconds":30},"tolerations":[],"topologySpreadConstraints":[],"updateStrategy":{"type":"RollingUpdate"},"volumeMounts":[],"volumes":[]}` | A common template applied to all deployments. Each deployment can then override individual fields as needed by the operator. | +| delegatedOperatorDeployments.template | object | `{"affinity":{},"deploymentAnnotations":{},"description":"","env":{"FIFTYONE_DELEGATED_OPERATION_LOG_PATH":"","FIFTYONE_INTERNAL_SERVICE":true,"FIFTYONE_MEDIA_CACHE_SIZE_BYTES":-1},"extraContainers":[],"image":{"pullPolicy":"Always","repository":"voxel51/fiftyone-teams-cv-full","tag":""},"labels":{},"liveness":{"failureThreshold":5,"periodSeconds":30,"timeoutSeconds":30},"nativeSidecarContainers":[],"nodeSelector":{},"podAnnotations":{},"podDisruptionBudget":{"enabled":false,"minAvailable":null},"podSecurityContext":{},"readiness":{"failureThreshold":5,"periodSeconds":30,"timeoutSeconds":30},"replicaCount":3,"resources":{"limits":{},"requests":{}},"secretEnv":{},"securityContext":{},"startup":{"failureThreshold":5,"periodSeconds":30,"timeoutSeconds":30},"tolerations":[],"topologySpreadConstraints":[],"updateStrategy":{"type":"RollingUpdate"},"volumeMounts":[],"volumes":[]}` | A common template applied to all deployments. Each deployment can then override individual fields as needed by the operator. | | delegatedOperatorDeployments.template.affinity | object | `{}` | Affinity and anti-affinity for `delegated-operator-executor`. [Reference][affinity]. | | delegatedOperatorDeployments.template.deploymentAnnotations | object | `{}` | Annotations for the `teams-do` deployment. [Reference][annotations]. | | delegatedOperatorDeployments.template.description | string | `""` | A description for the delegated operator instance. This is unused in the template context. Each operator should either set their own description or, optionally, use the default. If unset at the operator context, it will be defaulted to `Long running operations delegated to $name` where `$name` is the name of the Deployment object. | | delegatedOperatorDeployments.template.env.FIFTYONE_DELEGATED_OPERATION_LOG_PATH | string | `""` | Full path to a network-mounted file system or a cloud storage path to use for storing logs generated by delegated operation runs, one file per job. The default `""` means log upload is disabled. | | delegatedOperatorDeployments.template.env.FIFTYONE_INTERNAL_SERVICE | bool | `true` | Whether the SDK is running in an internal service context. When running in FiftyOne Enterprise, set to `true`. | | delegatedOperatorDeployments.template.env.FIFTYONE_MEDIA_CACHE_SIZE_BYTES | int | `-1` | Set the media cache size (in bytes) for the local FiftyOne Delegated Operator Executor processes. When not set, the app's default value is 32 GiB. `-1` disables the media cache garbage collection (and cache size is unlimited). | +| delegatedOperatorDeployments.template.extraContainers | list | `[]` | Additional containers to run in each delegated-operator-executor pod alongside the main container. When non-empty, the pod is configured with `shareProcessNamespace: true`. [Reference][sidecar-containers]. | | delegatedOperatorDeployments.template.image.pullPolicy | string | `"Always"` | Instruct when the kubelet should pull (download) the specified image. One of `IfNotPresent`, `Always` or `Never`. [Reference][image-pull-policy]. | | delegatedOperatorDeployments.template.image.repository | string | `"voxel51/fiftyone-teams-cv-full"` | Container image for `delegated-operator-executor`. | | delegatedOperatorDeployments.template.image.tag | string | `""` | Image tag for `delegated-operator-executor`. Defaults to the chart version. | @@ -897,6 +902,7 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): | delegatedOperatorDeployments.template.liveness.failureThreshold | int | `5` | Number of times to retry the liveness probe for the `teams-do`. [Reference][probes]. | | delegatedOperatorDeployments.template.liveness.periodSeconds | int | `30` | How often (in seconds) to perform the liveness probe for `teams-do`. [Reference][probes]. | | delegatedOperatorDeployments.template.liveness.timeoutSeconds | int | `30` | Timeout for the liveness probe for the `teams-do`. [Reference][probes]. | +| delegatedOperatorDeployments.template.nativeSidecarContainers | list | `[]` | Native sidecar containers for delegated-operator-executor pods (Kubernetes 1.29+). Entries are rendered under `initContainers`; set `restartPolicy: Always` on each entry to enable the native sidecar lifecycle. [Reference][sidecar-containers]. | | delegatedOperatorDeployments.template.nodeSelector | object | `{}` | nodeSelector for `delegated-operator-executor`. [Reference][node-selector]. | | delegatedOperatorDeployments.template.podAnnotations | object | `{}` | Annotations for delegated-operator-executor pods. [Reference][annotations]. | | delegatedOperatorDeployments.template.podDisruptionBudget.enabled | bool | `false` | Whether a pod disruption budget is enabled for `teams-plugins`. | @@ -922,7 +928,7 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): | delegatedOperatorJobTemplates.configMap.labels | object | `{}` | Additional labels for the generated `ConfigMap`. [Reference][labels-and-selectors]. | | delegatedOperatorJobTemplates.configMap.name | string | `""` | Name of the `ConfigMap` (existing or to be created) in the namespace `namespace.name` used for DO templates. Defaults to `release-name-fiftyone-teams-app-do-templates`. | | delegatedOperatorJobTemplates.jobs | object | `{}` | On-Demand Delegated Operator Jobs. | -| delegatedOperatorJobTemplates.template | object | `{"activeDeadlineSeconds":null,"affinity":{},"backoffLimit":null,"completions":null,"containerSecurityContext":{},"env":{"FIFTYONE_DELEGATED_OPERATION_LOG_PATH":"","FIFTYONE_MEDIA_CACHE_SIZE_BYTES":-1},"image":{"pullPolicy":"Always","repository":"voxel51/fiftyone-teams-cv-full","tag":""},"jobAnnotations":{},"labels":{},"nodeSelector":{},"podAnnotations":{},"podSecurityContext":{},"resources":{"limits":{},"requests":{}},"secretEnv":{},"tolerations":[],"ttlSecondsAfterFinished":null,"volumeMounts":[],"volumes":[]}` | A common template applied to all deployments. Each deployment can then override individual fields as needed by the operator. | +| delegatedOperatorJobTemplates.template | object | `{"activeDeadlineSeconds":null,"affinity":{},"backoffLimit":null,"completions":null,"containerSecurityContext":{},"env":{"FIFTYONE_DELEGATED_OPERATION_LOG_PATH":"","FIFTYONE_MEDIA_CACHE_SIZE_BYTES":-1},"extraContainers":[],"image":{"pullPolicy":"Always","repository":"voxel51/fiftyone-teams-cv-full","tag":""},"jobAnnotations":{},"labels":{},"nativeSidecarContainers":[],"nodeSelector":{},"podAnnotations":{},"podSecurityContext":{},"resources":{"limits":{},"requests":{}},"secretEnv":{},"tolerations":[],"ttlSecondsAfterFinished":null,"volumeMounts":[],"volumes":[]}` | A common template applied to all deployments. Each deployment can then override individual fields as needed by the operator. | | delegatedOperatorJobTemplates.template.activeDeadlineSeconds | optional | `nil` | Maximum of seconds a job should be able to run. [Reference][job-termination-and-cleanup]. | | delegatedOperatorJobTemplates.template.affinity | object | `{}` | Affinity and anti-affinity for `delegated-operator-executor`. [Reference][affinity]. | | delegatedOperatorJobTemplates.template.backoffLimit | optional | `nil` | Amount of retries for the job to try before being marked as "Failed". [Reference][job-backoff-failure-policy]. | @@ -930,11 +936,13 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): | delegatedOperatorJobTemplates.template.containerSecurityContext | object | `{}` | Container security configuration for generated delegated-operator containers. [Reference][container-security-context]. | | delegatedOperatorJobTemplates.template.env.FIFTYONE_DELEGATED_OPERATION_LOG_PATH | string | `""` | Full path to a network-mounted file system or a cloud storage path to use for storing logs generated by delegated operation runs, one file per job. The default `""` means log upload is disabled. | | delegatedOperatorJobTemplates.template.env.FIFTYONE_MEDIA_CACHE_SIZE_BYTES | int | `-1` | Set the media cache size (in bytes) for the local FiftyOne Delegated Operator Executor processes. When not set, the app's default value is 32 GiB. `-1` disables the media cache garbage collection (and cache size is unlimited). | +| delegatedOperatorJobTemplates.template.extraContainers | list | `[]` | Additional containers to run in each on-demand delegated-operator Job pod alongside the executor container. When non-empty, the pod is configured with `shareProcessNamespace: true`. Note: a regular container that does not exit will block Job completion; for telemetry sidecars use `nativeSidecarContainers` instead. [Reference][sidecar-containers]. | | delegatedOperatorJobTemplates.template.image.pullPolicy | string | `"Always"` | Instruct when the kubelet should pull (download) the specified image. One of `IfNotPresent`, `Always` or `Never`. [Reference][image-pull-policy]. | | delegatedOperatorJobTemplates.template.image.repository | string | `"voxel51/fiftyone-teams-cv-full"` | Container image for `delegated-operator-executor`. | | delegatedOperatorJobTemplates.template.image.tag | string | `""` | Image tag for `delegated-operator-executor`. Defaults to the chart version. | | delegatedOperatorJobTemplates.template.jobAnnotations | object | `{}` | Annotations for the `teams-do` deployment. [Reference][annotations]. | | delegatedOperatorJobTemplates.template.labels | object | `{}` | Additional labels for the `delegated-operator-executor` related objects. [Reference][labels-and-selectors]. | +| delegatedOperatorJobTemplates.template.nativeSidecarContainers | list | `[]` | Native sidecar containers for on-demand delegated-operator Job pods (Kubernetes 1.29+). Entries are rendered under `initContainers`; set `restartPolicy: Always` on each entry to enable the native sidecar lifecycle. Kubelet auto-terminates these when the executor container finishes, so the Job completes cleanly. [Reference][sidecar-containers]. | | delegatedOperatorJobTemplates.template.nodeSelector | object | `{}` | nodeSelector for `delegated-operator-executor`. [Reference][node-selector]. | | delegatedOperatorJobTemplates.template.podAnnotations | object | `{}` | Annotations for delegated-operator-executor pods. [Reference][annotations]. | | delegatedOperatorJobTemplates.template.podSecurityContext | object | `{}` | Pod-level security attributes and common container settings for `delegated-operator-executor`. [Reference][security-context]. | @@ -975,6 +983,7 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): | pluginsSettings.env.FIFTYONE_INTERNAL_SERVICE | bool | `true` | Whether the SDK is running in an internal service context. When running in FiftyOne Enterprise, set to `true`. | | pluginsSettings.env.FIFTYONE_MEDIA_CACHE_APP_IMAGES | bool | `false` | Controls whether cloud media images will be downloaded and added to the local cache upon viewing media in the app. | | pluginsSettings.env.FIFTYONE_MEDIA_CACHE_SIZE_BYTES | int | `-1` | Set the media cache size (in bytes) for the local FiftyOne Plugins processes. When not set, the app's default value is 32 GiB. `-1` disables the media cache garbage collection (and cache size is unlimited). | +| pluginsSettings.extraContainers | list | `[]` | Additional containers to run in the `teams-plugins` pod alongside the main container. When non-empty, the pod is configured with `shareProcessNamespace: true`. [Reference][sidecar-containers]. | | pluginsSettings.image.pullPolicy | string | `"Always"` | Instruct when the kubelet should pull (download) the specified image. One of `IfNotPresent`, `Always` or `Never`. [Reference][image-pull-policy]. | | pluginsSettings.image.repository | string | `"voxel51/fiftyone-app"` | Container image for `teams-plugins`. | | pluginsSettings.image.tag | string | `""` | Image tag for `teams-plugins`. Defaults to the chart version. | @@ -987,6 +996,7 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): | pluginsSettings.liveness.failureThreshold | int | `5` | Number of times to retry the liveness probe for the `teams-plugins`. [Reference][probes]. | | pluginsSettings.liveness.periodSeconds | int | `15` | How often (in seconds) to perform the liveness probe for `teams-plugins`. [Reference][probes]. | | pluginsSettings.liveness.timeoutSeconds | int | `5` | Timeout for the liveness probe for the `teams-plugins`. [Reference][probes]. | +| pluginsSettings.nativeSidecarContainers | list | `[]` | Native sidecar containers for the `teams-plugins` pod (Kubernetes 1.29+). Entries are rendered under `initContainers`; set `restartPolicy: Always` on each entry to enable the native sidecar lifecycle. [Reference][sidecar-containers]. | | pluginsSettings.nodeSelector | object | `{}` | nodeSelector for `teams-plugins`. [Reference][node-selector]. | | pluginsSettings.podAnnotations | object | `{}` | Annotations for `teams-plugins` pods. [Reference][annotations]. | | pluginsSettings.podDisruptionBudget | object | `{"enabled":false,"minAvailable":null}` | Pod Disruption Budget for pods for `teams-plugins`. [Reference][pod-disruption-budget]. | @@ -1114,6 +1124,7 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): [security-context]: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ [service-account]: https://kubernetes.io/docs/concepts/security/service-accounts/ [service-type]: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types +[sidecar-containers]: https://kubernetes.io/docs/concepts/workloads/pods/sidecar-containers/ [taints-and-tolerations]: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ [topology-spread-constraints]: https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/ [upgrade-strategies]: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy diff --git a/helm/fiftyone-teams-app/README.md.gotmpl b/helm/fiftyone-teams-app/README.md.gotmpl index fe3580178..a57059017 100644 --- a/helm/fiftyone-teams-app/README.md.gotmpl +++ b/helm/fiftyone-teams-app/README.md.gotmpl @@ -750,6 +750,7 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): [security-context]: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ [service-account]: https://kubernetes.io/docs/concepts/security/service-accounts/ [service-type]: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types +[sidecar-containers]: https://kubernetes.io/docs/concepts/workloads/pods/sidecar-containers/ [taints-and-tolerations]: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ [topology-spread-constraints]: https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/ [upgrade-strategies]: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy diff --git a/helm/fiftyone-teams-app/templates/delegated-operator-job-configmap.yaml b/helm/fiftyone-teams-app/templates/delegated-operator-job-configmap.yaml index 3ce459bdc..36ea5241b 100644 --- a/helm/fiftyone-teams-app/templates/delegated-operator-job-configmap.yaml +++ b/helm/fiftyone-teams-app/templates/delegated-operator-job-configmap.yaml @@ -42,6 +42,8 @@ data: {{- $mergedTolerations := $jobConfig.tolerations | default $baseTpl.tolerations }} {{- $mergedVolumeMounts := $jobConfig.volumeMounts | default $baseTpl.volumeMounts }} {{- $mergedVolumes := $jobConfig.volumes | default $baseTpl.volumes }} + {{- $mergedExtraContainers := $jobConfig.extraContainers | default $baseTpl.extraContainers }} + {{- $mergedNativeSidecarContainers := $jobConfig.nativeSidecarContainers | default $baseTpl.nativeSidecarContainers }} {{- /* Trunc at 48 for the random ID to be generated via API */ -}} {{- $metadataName := kebabcase $jobName | trunc 48 }} @@ -87,6 +89,9 @@ data: spec: restartPolicy: {{ $jobConfig.restartPolicy | default $baseTpl.restartPolicy | default "Never" }} serviceAccountName: {{ include "fiftyone-teams-app.serviceAccountName" $ }} + {{- if or $mergedExtraContainers $mergedNativeSidecarContainers }} + shareProcessNamespace: true + {{- end }} {{- with $mergedPodSecurityContext }} securityContext: {{- toYaml . | nindent 12 }} @@ -119,6 +124,13 @@ data: volumeMounts: {{- toYaml . | nindent 16 }} {{- end }} + {{- with $mergedExtraContainers }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $mergedNativeSidecarContainers }} + initContainers: + {{- toYaml . | nindent 12 }} + {{- end }} {{- with $mergedNodeSelector }} nodeSelector: {{- toYaml . | nindent 12 }} diff --git a/helm/fiftyone-teams-app/values.schema.json b/helm/fiftyone-teams-app/values.schema.json index 79ba1b8a4..3ab628be3 100644 --- a/helm/fiftyone-teams-app/values.schema.json +++ b/helm/fiftyone-teams-app/values.schema.json @@ -110,6 +110,15 @@ "title": "env", "type": "object" }, + "extraContainers": { + "description": "Additional containers to run in the `teams-api` pod alongside the\nmain container. When non-empty, the pod is configured with\n`shareProcessNamespace: true`. [Reference][sidecar-containers].", + "items": { + "required": [] + }, + "required": [], + "title": "extraContainers", + "type": "array" + }, "image": { "properties": { "pullPolicy": { @@ -348,6 +357,15 @@ "title": "liveness", "type": "object" }, + "nativeSidecarContainers": { + "description": "Native sidecar containers for the `teams-api` pod (Kubernetes 1.29+).\nEntries are rendered under `initContainers`; set `restartPolicy: Always`\non each entry to enable the native sidecar lifecycle. [Reference][sidecar-containers].", + "items": { + "required": [] + }, + "required": [], + "title": "nativeSidecarContainers", + "type": "array" + }, "nodeSelector": { "description": "nodeSelector for `teams-api`. [Reference][node-selector].", "required": [], @@ -843,6 +861,15 @@ "title": "env", "type": "object" }, + "extraContainers": { + "description": "Additional containers to run in the `fiftyone-app` pod alongside the\nmain container. When non-empty, the pod is configured with\n`shareProcessNamespace: true`. [Reference][sidecar-containers].", + "items": { + "required": [] + }, + "required": [], + "title": "extraContainers", + "type": "array" + }, "image": { "properties": { "pullPolicy": { @@ -1078,6 +1105,15 @@ "title": "liveness", "type": "object" }, + "nativeSidecarContainers": { + "description": "Native sidecar containers for the `fiftyone-app` pod (Kubernetes 1.29+).\nEntries are rendered under `initContainers`; set `restartPolicy: Always`\non each entry to enable the native sidecar lifecycle. [Reference][sidecar-containers].", + "items": { + "required": [] + }, + "required": [], + "title": "nativeSidecarContainers", + "type": "array" + }, "nodeSelector": { "description": "nodeSelector for `fiftyone-app`. [Reference][node-selector].", "required": [], @@ -2166,6 +2202,15 @@ "title": "env", "type": "object" }, + "extraContainers": { + "description": "Additional containers to run in each delegated-operator-executor pod\nalongside the main container. When non-empty, the pod is configured\nwith `shareProcessNamespace: true`. [Reference][sidecar-containers].", + "items": { + "required": [] + }, + "required": [], + "title": "extraContainers", + "type": "array" + }, "image": { "properties": { "pullPolicy": { @@ -2237,6 +2282,15 @@ "title": "liveness", "type": "object" }, + "nativeSidecarContainers": { + "description": "Native sidecar containers for delegated-operator-executor pods\n(Kubernetes 1.29+). Entries are rendered under `initContainers`; set\n`restartPolicy: Always` on each entry to enable the native sidecar\nlifecycle. [Reference][sidecar-containers].", + "items": { + "required": [] + }, + "required": [], + "title": "nativeSidecarContainers", + "type": "array" + }, "nodeSelector": { "description": "nodeSelector for `delegated-operator-executor`. [Reference][node-selector].", "required": [], @@ -2547,6 +2601,15 @@ "title": "env", "type": "object" }, + "extraContainers": { + "description": "Additional containers to run in each on-demand delegated-operator Job\npod alongside the executor container. When non-empty, the pod is\nconfigured with `shareProcessNamespace: true`. Note: a regular\ncontainer that does not exit will block Job completion; for telemetry\nsidecars use `nativeSidecarContainers` instead. [Reference][sidecar-containers].", + "items": { + "required": [] + }, + "required": [], + "title": "extraContainers", + "type": "array" + }, "image": { "properties": { "pullPolicy": { @@ -2596,6 +2659,15 @@ "title": "labels", "type": "object" }, + "nativeSidecarContainers": { + "description": "Native sidecar containers for on-demand delegated-operator Job pods\n(Kubernetes 1.29+). Entries are rendered under `initContainers`; set\n`restartPolicy: Always` on each entry to enable the native sidecar\nlifecycle. Kubelet auto-terminates these when the executor container\nfinishes, so the Job completes cleanly. [Reference][sidecar-containers].", + "items": { + "required": [] + }, + "required": [], + "title": "nativeSidecarContainers", + "type": "array" + }, "nodeSelector": { "description": "nodeSelector for `delegated-operator-executor`. [Reference][node-selector].", "required": [], @@ -2984,6 +3056,15 @@ "title": "env", "type": "object" }, + "extraContainers": { + "description": "Additional containers to run in the `teams-plugins` pod alongside the\nmain container. When non-empty, the pod is configured with\n`shareProcessNamespace: true`. [Reference][sidecar-containers].", + "items": { + "required": [] + }, + "required": [], + "title": "extraContainers", + "type": "array" + }, "image": { "properties": { "pullPolicy": { @@ -3219,6 +3300,15 @@ "title": "liveness", "type": "object" }, + "nativeSidecarContainers": { + "description": "Native sidecar containers for the `teams-plugins` pod (Kubernetes 1.29+).\nEntries are rendered under `initContainers`; set `restartPolicy: Always`\non each entry to enable the native sidecar lifecycle. [Reference][sidecar-containers].", + "items": { + "required": [] + }, + "required": [], + "title": "nativeSidecarContainers", + "type": "array" + }, "nodeSelector": { "description": "nodeSelector for `teams-plugins`. [Reference][node-selector].", "required": [], diff --git a/helm/fiftyone-teams-app/values.yaml b/helm/fiftyone-teams-app/values.yaml index 93ef59e5e..cec49ac43 100644 --- a/helm/fiftyone-teams-app/values.yaml +++ b/helm/fiftyone-teams-app/values.yaml @@ -120,6 +120,14 @@ apiSettings: # -- Affinity and anti-affinity for `teams-api`. [Reference][affinity]. affinity: {} + # -- Additional containers to run in the `teams-api` pod alongside the + # main container. When non-empty, the pod is configured with + # `shareProcessNamespace: true`. [Reference][sidecar-containers]. + extraContainers: [] + # -- Native sidecar containers for the `teams-api` pod (Kubernetes 1.29+). + # Entries are rendered under `initContainers`; set `restartPolicy: Always` + # on each entry to enable the native sidecar lifecycle. [Reference][sidecar-containers]. + nativeSidecarContainers: [] initContainers: # -- Container security configuration for `teams-api` `initContainers`. [Reference][container-security-context]. containerSecurityContext: @@ -390,6 +398,14 @@ appSettings: # -- Affinity and anti-affinity for `fiftyone-app`. [Reference][affinity]. affinity: {} + # -- Additional containers to run in the `fiftyone-app` pod alongside the + # main container. When non-empty, the pod is configured with + # `shareProcessNamespace: true`. [Reference][sidecar-containers]. + extraContainers: [] + # -- Native sidecar containers for the `fiftyone-app` pod (Kubernetes 1.29+). + # Entries are rendered under `initContainers`; set `restartPolicy: Always` + # on each entry to enable the native sidecar lifecycle. [Reference][sidecar-containers]. + nativeSidecarContainers: [] initContainers: # -- Container security configuration for `fiftyone-app` `initContainers`. [Reference][container-security-context]. containerSecurityContext: @@ -820,6 +836,15 @@ delegatedOperatorDeployments: volumeMounts: [] # -- Volumes for `delegated-operator-executor`. [Reference][volumes]. volumes: [] + # -- Additional containers to run in each delegated-operator-executor pod + # alongside the main container. When non-empty, the pod is configured + # with `shareProcessNamespace: true`. [Reference][sidecar-containers]. + extraContainers: [] + # -- Native sidecar containers for delegated-operator-executor pods + # (Kubernetes 1.29+). Entries are rendered under `initContainers`; set + # `restartPolicy: Always` on each entry to enable the native sidecar + # lifecycle. [Reference][sidecar-containers]. + nativeSidecarContainers: [] # -- Additional deployments to configure. Each template # will use .Values.delegatedOperatorDeployments.template as a base. @@ -996,6 +1021,18 @@ delegatedOperatorJobTemplates: volumeMounts: [] # -- Volumes for `delegated-operator-executor`. [Reference][volumes]. volumes: [] + # -- Additional containers to run in each on-demand delegated-operator Job + # pod alongside the executor container. When non-empty, the pod is + # configured with `shareProcessNamespace: true`. Note: a regular + # container that does not exit will block Job completion; for telemetry + # sidecars use `nativeSidecarContainers` instead. [Reference][sidecar-containers]. + extraContainers: [] + # -- Native sidecar containers for on-demand delegated-operator Job pods + # (Kubernetes 1.29+). Entries are rendered under `initContainers`; set + # `restartPolicy: Always` on each entry to enable the native sidecar + # lifecycle. Kubelet auto-terminates these when the executor container + # finishes, so the Job completes cleanly. [Reference][sidecar-containers]. + nativeSidecarContainers: [] # -- On-Demand Delegated Operator Jobs. jobs: {} # Default CPU-only batch job @@ -1227,6 +1264,14 @@ pluginsSettings: # -- Affinity and anti-affinity for `teams-plugins`. [Reference][affinity]. affinity: {} + # -- Additional containers to run in the `teams-plugins` pod alongside the + # main container. When non-empty, the pod is configured with + # `shareProcessNamespace: true`. [Reference][sidecar-containers]. + extraContainers: [] + # -- Native sidecar containers for the `teams-plugins` pod (Kubernetes 1.29+). + # Entries are rendered under `initContainers`; set `restartPolicy: Always` + # on each entry to enable the native sidecar lifecycle. [Reference][sidecar-containers]. + nativeSidecarContainers: [] initContainers: # -- Container security configuration for `teams-plugins` `initContainers`. [Reference][container-security-context]. containerSecurityContext: diff --git a/tests/unit/compose/docker-compose-internal-auth_test.go b/tests/unit/compose/docker-compose-internal-auth_test.go index 7b404eab2..b58f65498 100644 --- a/tests/unit/compose/docker-compose-internal-auth_test.go +++ b/tests/unit/compose/docker-compose-internal-auth_test.go @@ -28,7 +28,9 @@ var internalAuthComposeFile = filepath.Join(dockerInternalAuthDir, "compose.yaml var internalAuthComposePluginsFile = filepath.Join(dockerInternalAuthDir, "compose.plugins.yaml") var internalAuthComposeDedicatedPluginsFile = filepath.Join(dockerInternalAuthDir, "compose.dedicated-plugins.yaml") var internalAuthComposeDelegatedOperationsFile = filepath.Join(dockerInternalAuthDir, "compose.delegated-operators.yaml") +var internalAuthComposeDelegatedOperationsGpuFile = filepath.Join(dockerInternalAuthDir, "compose.delegated-operators.gpu.yaml") var internalAuthComposeTelemetryFile = filepath.Join(dockerInternalAuthDir, "compose.telemetry.yaml") +var internalAuthComposeTelemetryPluginsFile = filepath.Join(dockerInternalAuthDir, "compose.telemetry.plugins.yaml") var internalAuthComposeTelemetryDelegatedOperatorsFile = filepath.Join(dockerInternalAuthDir, "compose.telemetry.delegated-operators.yaml") var internalAuthEnvTemplateFilePath = filepath.Join(dockerInternalAuthDir, "env.template") @@ -61,12 +63,14 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServicesNames() { name string configPaths []string // file paths to one or more Compose files. envFiles []string // file paths to ".env" files with additional environment variable data + profiles []string // compose profiles to activate expected []string }{ { "compose", []string{internalAuthComposeFile}, s.dotEnvFiles, + nil, []string{ "fiftyone-app", "teams-api", @@ -78,6 +82,7 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServicesNames() { "composePlugins", []string{internalAuthComposePluginsFile}, s.dotEnvFiles, + nil, []string{ "fiftyone-app", "teams-api", @@ -89,6 +94,7 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServicesNames() { "composeDedicatedPlugins", []string{internalAuthComposeDedicatedPluginsFile}, s.dotEnvFiles, + nil, []string{ "fiftyone-app", "teams-api", @@ -101,14 +107,30 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServicesNames() { "composeDelegatedOperations", []string{internalAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, + nil, []string{ "teams-do", }, }, + { + "composeDelegatedOperationsGpu", + []string{ + internalAuthComposeDelegatedOperationsFile, + internalAuthComposeDelegatedOperationsGpuFile, + }, + s.dotEnvFiles, + []string{"gpu"}, + []string{ + "teams-do", + "teams-do-gpu", + "teams-do-gpu-telemetry", + }, + }, { "composeTelemetry", []string{internalAuthComposeFile, internalAuthComposeTelemetryFile}, s.dotEnvFiles, + nil, []string{ "fiftyone-app", "fiftyone-app-telemetry", @@ -119,6 +141,28 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServicesNames() { "telemetry-redis", }, }, + { + "composeTelemetryPlugins", + []string{ + internalAuthComposeFile, + internalAuthComposeDedicatedPluginsFile, + internalAuthComposeTelemetryFile, + internalAuthComposeTelemetryPluginsFile, + }, + s.dotEnvFiles, + nil, + []string{ + "fiftyone-app", + "fiftyone-app-telemetry", + "teams-api", + "teams-api-telemetry", + "teams-app", + "teams-cas", + "teams-plugins", + "teams-plugins-telemetry", + "telemetry-redis", + }, + }, { "composeTelemetryDelegatedOperators", []string{ @@ -128,6 +172,7 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServicesNames() { internalAuthComposeTelemetryDelegatedOperatorsFile, }, s.dotEnvFiles, + nil, []string{ "fiftyone-app", "fiftyone-app-telemetry", @@ -155,6 +200,7 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServicesNames() { cli.WithName(s.projectName), cli.WithEnvFiles(testCase.envFiles...), cli.WithDotEnv, + cli.WithProfiles(testCase.profiles), ) s.NoError(err) diff --git a/tests/unit/compose/docker-compose-legacy-auth_test.go b/tests/unit/compose/docker-compose-legacy-auth_test.go index 39c7c2c8e..1b38cb953 100644 --- a/tests/unit/compose/docker-compose-legacy-auth_test.go +++ b/tests/unit/compose/docker-compose-legacy-auth_test.go @@ -28,7 +28,9 @@ var legacyAuthComposeFile = filepath.Join(dockerLegacyAuthDir, "compose.yaml") var legacyAuthComposePluginsFile = filepath.Join(dockerLegacyAuthDir, "compose.plugins.yaml") var legacyAuthComposeDedicatedPluginsFile = filepath.Join(dockerLegacyAuthDir, "compose.dedicated-plugins.yaml") var legacyAuthComposeDelegatedOperationsFile = filepath.Join(dockerLegacyAuthDir, "compose.delegated-operators.yaml") +var legacyAuthComposeDelegatedOperationsGpuFile = filepath.Join(dockerLegacyAuthDir, "compose.delegated-operators.gpu.yaml") var legacyAuthComposeTelemetryFile = filepath.Join(dockerLegacyAuthDir, "compose.telemetry.yaml") +var legacyAuthComposeTelemetryPluginsFile = filepath.Join(dockerLegacyAuthDir, "compose.telemetry.plugins.yaml") var legacyAuthComposeTelemetryDelegatedOperatorsFile = filepath.Join(dockerLegacyAuthDir, "compose.telemetry.delegated-operators.yaml") var legacyAuthEnvTemplateFilePath = filepath.Join(dockerLegacyAuthDir, "env.template") @@ -61,12 +63,14 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServicesNames() { name string configPaths []string // file paths to one or more Compose files. envFiles []string // file paths to ".env" files with additional environment variable data + profiles []string // compose profiles to activate expected []string }{ { "compose", []string{legacyAuthComposeFile}, s.dotEnvFiles, + nil, []string{ "fiftyone-app", "teams-api", @@ -78,6 +82,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServicesNames() { "composePlugins", []string{legacyAuthComposePluginsFile}, s.dotEnvFiles, + nil, []string{ "fiftyone-app", "teams-api", @@ -89,6 +94,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServicesNames() { "composeDedicatedPlugins", []string{legacyAuthComposeDedicatedPluginsFile}, s.dotEnvFiles, + nil, []string{ "fiftyone-app", "teams-api", @@ -101,14 +107,30 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServicesNames() { "composeDelegatedOperations", []string{legacyAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, + nil, []string{ "teams-do", }, }, + { + "composeDelegatedOperationsGpu", + []string{ + legacyAuthComposeDelegatedOperationsFile, + legacyAuthComposeDelegatedOperationsGpuFile, + }, + s.dotEnvFiles, + []string{"gpu"}, + []string{ + "teams-do", + "teams-do-gpu", + "teams-do-gpu-telemetry", + }, + }, { "composeTelemetry", []string{legacyAuthComposeFile, legacyAuthComposeTelemetryFile}, s.dotEnvFiles, + nil, []string{ "fiftyone-app", "fiftyone-app-telemetry", @@ -119,6 +141,28 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServicesNames() { "telemetry-redis", }, }, + { + "composeTelemetryPlugins", + []string{ + legacyAuthComposeFile, + legacyAuthComposeDedicatedPluginsFile, + legacyAuthComposeTelemetryFile, + legacyAuthComposeTelemetryPluginsFile, + }, + s.dotEnvFiles, + nil, + []string{ + "fiftyone-app", + "fiftyone-app-telemetry", + "teams-api", + "teams-api-telemetry", + "teams-app", + "teams-cas", + "teams-plugins", + "teams-plugins-telemetry", + "telemetry-redis", + }, + }, { "composeTelemetryDelegatedOperators", []string{ @@ -128,6 +172,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServicesNames() { legacyAuthComposeTelemetryDelegatedOperatorsFile, }, s.dotEnvFiles, + nil, []string{ "fiftyone-app", "fiftyone-app-telemetry", @@ -155,6 +200,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServicesNames() { cli.WithName(s.projectName), cli.WithEnvFiles(testCase.envFiles...), cli.WithDotEnv, + cli.WithProfiles(testCase.profiles), ) s.NoError(err) From abc7cf73d2e761da1daa2952f4a0e3d4a0c9c38b Mon Sep 17 00:00:00 2001 From: kacey Date: Fri, 15 May 2026 00:10:47 -0700 Subject: [PATCH 09/23] chore: fix tests --- docker/docs/configuring-telemetry.md | 5 +++-- tests/unit/compose/docker-compose-internal-auth_test.go | 9 +++++++++ tests/unit/compose/docker-compose-legacy-auth_test.go | 9 +++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/docker/docs/configuring-telemetry.md b/docker/docs/configuring-telemetry.md index 664f8211c..0ecadcdac 100644 --- a/docker/docs/configuring-telemetry.md +++ b/docker/docs/configuring-telemetry.md @@ -73,8 +73,9 @@ docker compose --profile gpu \ with the `SYS_PTRACE` capability so py-spy can attach to the target. - `teams-plugins-telemetry` (only with `compose.telemetry.plugins.yaml`) — sidecar for the dedicated `teams-plugins` service. -- `teams-do-telemetry` (only with `compose.telemetry.delegated-operators.yaml`) — - sidecar in `EXECUTOR_SIDECAR=true` mode that watches the executor for +- `teams-do-telemetry` (only with + `compose.telemetry.delegated-operators.yaml`) — sidecar in + `EXECUTOR_SIDECAR=true` mode that watches the executor for per-operation child processes and records per-op metrics back to the `delegated_ops` MongoDB document. - `teams-do-gpu` + `teams-do-gpu-telemetry` (only with diff --git a/tests/unit/compose/docker-compose-internal-auth_test.go b/tests/unit/compose/docker-compose-internal-auth_test.go index 869578b2a..cc9f911bc 100644 --- a/tests/unit/compose/docker-compose-internal-auth_test.go +++ b/tests/unit/compose/docker-compose-internal-auth_test.go @@ -115,15 +115,24 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServicesNames() { { "composeDelegatedOperationsGpu", []string{ + internalAuthComposeFile, internalAuthComposeDelegatedOperationsFile, internalAuthComposeDelegatedOperationsGpuFile, + internalAuthComposeTelemetryFile, }, s.dotEnvFiles, []string{"gpu"}, []string{ + "fiftyone-app", + "fiftyone-app-telemetry", + "teams-api", + "teams-api-telemetry", + "teams-app", + "teams-cas", "teams-do", "teams-do-gpu", "teams-do-gpu-telemetry", + "telemetry-redis", }, }, { diff --git a/tests/unit/compose/docker-compose-legacy-auth_test.go b/tests/unit/compose/docker-compose-legacy-auth_test.go index 699602299..c44c2fb86 100644 --- a/tests/unit/compose/docker-compose-legacy-auth_test.go +++ b/tests/unit/compose/docker-compose-legacy-auth_test.go @@ -115,15 +115,24 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServicesNames() { { "composeDelegatedOperationsGpu", []string{ + legacyAuthComposeFile, legacyAuthComposeDelegatedOperationsFile, legacyAuthComposeDelegatedOperationsGpuFile, + legacyAuthComposeTelemetryFile, }, s.dotEnvFiles, []string{"gpu"}, []string{ + "fiftyone-app", + "fiftyone-app-telemetry", + "teams-api", + "teams-api-telemetry", + "teams-app", + "teams-cas", "teams-do", "teams-do-gpu", "teams-do-gpu-telemetry", + "telemetry-redis", }, }, { From b4b8beb754559613150ea9bc64cc6aec8c2238bd Mon Sep 17 00:00:00 2001 From: kacey Date: Fri, 15 May 2026 00:44:16 -0700 Subject: [PATCH 10/23] chore: fix tests --- helm/fiftyone-teams-app/values.schema.json | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/helm/fiftyone-teams-app/values.schema.json b/helm/fiftyone-teams-app/values.schema.json index 2b443cfe0..dd62ddaf6 100644 --- a/helm/fiftyone-teams-app/values.schema.json +++ b/helm/fiftyone-teams-app/values.schema.json @@ -107,7 +107,6 @@ "items": { "required": [] }, - "required": [], "title": "extraContainers", "type": "array" }, @@ -342,7 +341,6 @@ "items": { "required": [] }, - "required": [], "title": "nativeSidecarContainers", "type": "array" }, @@ -817,7 +815,6 @@ "items": { "required": [] }, - "required": [], "title": "extraContainers", "type": "array" }, @@ -1048,7 +1045,6 @@ "items": { "required": [] }, - "required": [], "title": "nativeSidecarContainers", "type": "array" }, @@ -2066,7 +2062,6 @@ "items": { "required": [] }, - "required": [], "title": "extraContainers", "type": "array" }, @@ -2141,7 +2136,6 @@ "items": { "required": [] }, - "required": [], "title": "nativeSidecarContainers", "type": "array" }, @@ -2444,7 +2438,6 @@ "items": { "required": [] }, - "required": [], "title": "extraContainers", "type": "array" }, @@ -2500,7 +2493,6 @@ "items": { "required": [] }, - "required": [], "title": "nativeSidecarContainers", "type": "array" }, @@ -2868,7 +2860,6 @@ "items": { "required": [] }, - "required": [], "title": "extraContainers", "type": "array" }, @@ -3099,7 +3090,6 @@ "items": { "required": [] }, - "required": [], "title": "nativeSidecarContainers", "type": "array" }, From c42832d3e942a369de4c42f3a4658e446f682753 Mon Sep 17 00:00:00 2001 From: kacey Date: Fri, 15 May 2026 20:55:54 -0700 Subject: [PATCH 11/23] chore: trigger ci From 03a4b78ba123f2acc27f447672a8a5aab08f1c95 Mon Sep 17 00:00:00 2001 From: kacey Date: Sat, 16 May 2026 22:02:14 -0700 Subject: [PATCH 12/23] fix: auto-inject sidecar settings --- helm/fiftyone-teams-app/README.md | 2 +- .../delegated-operator-job-configmap.yaml | 40 ++++++++++++++++++- helm/fiftyone-teams-app/values.schema.json | 2 +- helm/fiftyone-teams-app/values.yaml | 4 +- 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/helm/fiftyone-teams-app/README.md b/helm/fiftyone-teams-app/README.md index 5447b430f..391832585 100644 --- a/helm/fiftyone-teams-app/README.md +++ b/helm/fiftyone-teams-app/README.md @@ -943,7 +943,7 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): | delegatedOperatorJobTemplates.template.image.tag | string | `""` | Image tag for `delegated-operator-executor`. Defaults to the chart version. | | delegatedOperatorJobTemplates.template.jobAnnotations | object | `{}` | Annotations for the `teams-do` deployment. [Reference][annotations]. | | delegatedOperatorJobTemplates.template.labels | object | `{}` | Additional labels for the `delegated-operator-executor` related objects. [Reference][labels-and-selectors]. | -| delegatedOperatorJobTemplates.template.nativeSidecarContainers | list | `[]` | Native sidecar containers for on-demand delegated-operator Job pods (Kubernetes 1.29+). Entries are rendered under `initContainers`; set `restartPolicy: Always` on each entry to enable the native sidecar lifecycle. Kubelet auto-terminates these when the executor container finishes, so the Job completes cleanly. [Reference][sidecar-containers]. | +| delegatedOperatorJobTemplates.template.nativeSidecarContainers | list | `[]` | Native sidecar containers for on-demand delegated-operator Job pods (Kubernetes 1.29+). Entries are rendered under `initContainers`; set `restartPolicy: Always` on each entry to enable the native sidecar lifecycle. Kubelet auto-terminates these when the executor container finishes, so the Job completes cleanly. The chart auto-injects `TARGET_CONTAINER=executor` on each entry (override by setting it explicitly). [Reference][sidecar-containers]. | | delegatedOperatorJobTemplates.template.nodeSelector | object | `{}` | nodeSelector for `delegated-operator-executor`. [Reference][node-selector]. | | delegatedOperatorJobTemplates.template.podAnnotations | object | `{}` | Annotations for delegated-operator-executor pods. [Reference][annotations]. | | delegatedOperatorJobTemplates.template.podSecurityContext | object | `{}` | Pod-level security attributes and common container settings for `delegated-operator-executor`. [Reference][security-context]. | diff --git a/helm/fiftyone-teams-app/templates/delegated-operator-job-configmap.yaml b/helm/fiftyone-teams-app/templates/delegated-operator-job-configmap.yaml index 36ea5241b..e62c5de1d 100644 --- a/helm/fiftyone-teams-app/templates/delegated-operator-job-configmap.yaml +++ b/helm/fiftyone-teams-app/templates/delegated-operator-job-configmap.yaml @@ -44,6 +44,31 @@ data: {{- $mergedVolumes := $jobConfig.volumes | default $baseTpl.volumes }} {{- $mergedExtraContainers := $jobConfig.extraContainers | default $baseTpl.extraContainers }} {{- $mergedNativeSidecarContainers := $jobConfig.nativeSidecarContainers | default $baseTpl.nativeSidecarContainers }} + + {{- /* telemetry-sidecar needs a shared emptyDir; values can't reliably keep it through fleet regenerators, so reconcile it here. */ -}} + {{- $needsTelemetrySocket := false }} + {{- range $sidecar := $mergedNativeSidecarContainers }} + {{- range $vm := ($sidecar.volumeMounts | default list) }} + {{- if eq $vm.name "telemetry-socket" }}{{- $needsTelemetrySocket = true }}{{- end }} + {{- end }} + {{- end }} + {{- if $needsTelemetrySocket }} + {{- $hasVolume := false }} + {{- range $v := ($mergedVolumes | default list) }} + {{- if eq $v.name "telemetry-socket" }}{{- $hasVolume = true }}{{- end }} + {{- end }} + {{- if not $hasVolume }} + {{- $mergedVolumes = append ($mergedVolumes | default list) (dict "name" "telemetry-socket" "emptyDir" dict) }} + {{- end }} + {{- $hasMount := false }} + {{- range $vm := ($mergedVolumeMounts | default list) }} + {{- if eq $vm.name "telemetry-socket" }}{{- $hasMount = true }}{{- end }} + {{- end }} + {{- if not $hasMount }} + {{- $mergedVolumeMounts = append ($mergedVolumeMounts | default list) (dict "name" "telemetry-socket" "mountPath" "/tmp/telemetry") }} + {{- end }} + {{- end }} + {{- /* Trunc at 48 for the random ID to be generated via API */ -}} {{- $metadataName := kebabcase $jobName | trunc 48 }} @@ -129,7 +154,20 @@ data: {{- end }} {{- with $mergedNativeSidecarContainers }} initContainers: - {{- toYaml . | nindent 12 }} + {{- /* TARGET_CONTAINER tells the telemetry sidecar's log tailer which container to follow; the executor is hardcoded so we know the value. */ -}} + {{- range $sidecar := . }} + {{- $hasTargetContainer := false }} + {{- range $env := ($sidecar.env | default list) }} + {{- if eq $env.name "TARGET_CONTAINER" }} + {{- $hasTargetContainer = true }} + {{- end }} + {{- end }} + {{- $rendered := deepCopy $sidecar }} + {{- if not $hasTargetContainer }} + {{- $_ := set $rendered "env" (prepend ($sidecar.env | default list) (dict "name" "TARGET_CONTAINER" "value" "executor")) }} + {{- end }} + {{- list $rendered | toYaml | nindent 12 }} + {{- end }} {{- end }} {{- with $mergedNodeSelector }} nodeSelector: diff --git a/helm/fiftyone-teams-app/values.schema.json b/helm/fiftyone-teams-app/values.schema.json index dd62ddaf6..3f7b2b92a 100644 --- a/helm/fiftyone-teams-app/values.schema.json +++ b/helm/fiftyone-teams-app/values.schema.json @@ -2489,7 +2489,7 @@ "type": "object" }, "nativeSidecarContainers": { - "description": "Native sidecar containers for on-demand delegated-operator Job pods\n(Kubernetes 1.29+). Entries are rendered under `initContainers`; set\n`restartPolicy: Always` on each entry to enable the native sidecar\nlifecycle. Kubelet auto-terminates these when the executor container\nfinishes, so the Job completes cleanly. [Reference][sidecar-containers].", + "description": "Native sidecar containers for on-demand delegated-operator Job pods\n(Kubernetes 1.29+). Entries are rendered under `initContainers`; set\n`restartPolicy: Always` on each entry to enable the native sidecar\nlifecycle. Kubelet auto-terminates these when the executor container\nfinishes, so the Job completes cleanly. The chart auto-injects\n`TARGET_CONTAINER=executor` on each entry (override by setting it\nexplicitly). [Reference][sidecar-containers].", "items": { "required": [] }, diff --git a/helm/fiftyone-teams-app/values.yaml b/helm/fiftyone-teams-app/values.yaml index 429b3ce0a..5a8e18603 100644 --- a/helm/fiftyone-teams-app/values.yaml +++ b/helm/fiftyone-teams-app/values.yaml @@ -1031,7 +1031,9 @@ delegatedOperatorJobTemplates: # (Kubernetes 1.29+). Entries are rendered under `initContainers`; set # `restartPolicy: Always` on each entry to enable the native sidecar # lifecycle. Kubelet auto-terminates these when the executor container - # finishes, so the Job completes cleanly. [Reference][sidecar-containers]. + # finishes, so the Job completes cleanly. The chart auto-injects + # `TARGET_CONTAINER=executor` on each entry (override by setting it + # explicitly). [Reference][sidecar-containers]. nativeSidecarContainers: [] # -- On-Demand Delegated Operator Jobs. jobs: {} From 3d61c0e0554b6d31cf6f7405d4a2194d1702f489 Mon Sep 17 00:00:00 2001 From: kacey Date: Mon, 18 May 2026 07:05:42 -0700 Subject: [PATCH 13/23] fix: fix auto inject sidecar vars and update test --- .../delegated-operator-job-configmap.yaml | 25 ++-- .../delegated-operator-job-configmap_test.go | 138 ++++++++++++++++++ 2 files changed, 154 insertions(+), 9 deletions(-) diff --git a/helm/fiftyone-teams-app/templates/delegated-operator-job-configmap.yaml b/helm/fiftyone-teams-app/templates/delegated-operator-job-configmap.yaml index e62c5de1d..b68661a8b 100644 --- a/helm/fiftyone-teams-app/templates/delegated-operator-job-configmap.yaml +++ b/helm/fiftyone-teams-app/templates/delegated-operator-job-configmap.yaml @@ -45,14 +45,13 @@ data: {{- $mergedExtraContainers := $jobConfig.extraContainers | default $baseTpl.extraContainers }} {{- $mergedNativeSidecarContainers := $jobConfig.nativeSidecarContainers | default $baseTpl.nativeSidecarContainers }} - {{- /* telemetry-sidecar needs a shared emptyDir; values can't reliably keep it through fleet regenerators, so reconcile it here. */ -}} - {{- $needsTelemetrySocket := false }} + {{- $hasExecutorSidecar := false }} {{- range $sidecar := $mergedNativeSidecarContainers }} - {{- range $vm := ($sidecar.volumeMounts | default list) }} - {{- if eq $vm.name "telemetry-socket" }}{{- $needsTelemetrySocket = true }}{{- end }} + {{- range $env := ($sidecar.env | default list) }} + {{- if and (eq $env.name "EXECUTOR_SIDECAR") (eq (toString $env.value) "true") }}{{- $hasExecutorSidecar = true }}{{- end }} {{- end }} {{- end }} - {{- if $needsTelemetrySocket }} + {{- if $hasExecutorSidecar }} {{- $hasVolume := false }} {{- range $v := ($mergedVolumes | default list) }} {{- if eq $v.name "telemetry-socket" }}{{- $hasVolume = true }}{{- end }} @@ -154,18 +153,26 @@ data: {{- end }} {{- with $mergedNativeSidecarContainers }} initContainers: - {{- /* TARGET_CONTAINER tells the telemetry sidecar's log tailer which container to follow; the executor is hardcoded so we know the value. */ -}} {{- range $sidecar := . }} + {{- $isExecutor := false }} {{- $hasTargetContainer := false }} {{- range $env := ($sidecar.env | default list) }} - {{- if eq $env.name "TARGET_CONTAINER" }} - {{- $hasTargetContainer = true }} - {{- end }} + {{- if eq $env.name "TARGET_CONTAINER" }}{{- $hasTargetContainer = true }}{{- end }} + {{- if and (eq $env.name "EXECUTOR_SIDECAR") (eq (toString $env.value) "true") }}{{- $isExecutor = true }}{{- end }} {{- end }} {{- $rendered := deepCopy $sidecar }} {{- if not $hasTargetContainer }} {{- $_ := set $rendered "env" (prepend ($sidecar.env | default list) (dict "name" "TARGET_CONTAINER" "value" "executor")) }} {{- end }} + {{- if $isExecutor }} + {{- $hasSocketMount := false }} + {{- range $vm := ($sidecar.volumeMounts | default list) }} + {{- if eq $vm.name "telemetry-socket" }}{{- $hasSocketMount = true }}{{- end }} + {{- end }} + {{- if not $hasSocketMount }} + {{- $_ := set $rendered "volumeMounts" (append ($sidecar.volumeMounts | default list) (dict "name" "telemetry-socket" "mountPath" "/tmp/telemetry")) }} + {{- end }} + {{- end }} {{- list $rendered | toYaml | nindent 12 }} {{- end }} {{- end }} diff --git a/tests/unit/helm/delegated-operator-job-configmap_test.go b/tests/unit/helm/delegated-operator-job-configmap_test.go index 9ab313a4a..345bc2d4b 100644 --- a/tests/unit/helm/delegated-operator-job-configmap_test.go +++ b/tests/unit/helm/delegated-operator-job-configmap_test.go @@ -657,3 +657,141 @@ func (s *doK8sConfigMapTemplateTest) loadTestFile(filename, chartVersion string) result = strings.TrimSpace(result) return strings.TrimSpace(result) } + +func (s *doK8sConfigMapTemplateTest) TestNativeSidecarTelemetryAutoInject() { + testCases := []struct { + name string + values map[string]string + expected func(subT gruntworkTesting.TestingT, job batchv1.Job) + }{ + { + "AutoInjectsTelemetrySocketWiringWhenSidecarHasExecutorEnv", + map[string]string{ + "delegatedOperatorJobTemplates.jobs.testJob.unused": "nil", + "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].name": "telemetry-sidecar", + "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].image": "telemetry-sidecar:latest", + "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].restartPolicy": "Always", + "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].env[0].name": "EXECUTOR_SIDECAR", + "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].env[0].value": "true", + }, + func(subT gruntworkTesting.TestingT, job batchv1.Job) { + podSpec := job.Spec.Template.Spec + + s.Require().NotNil(podSpec.ShareProcessNamespace, "shareProcessNamespace must be set when nativeSidecarContainers present") + s.True(*podSpec.ShareProcessNamespace, "shareProcessNamespace must be true") + + s.Require().Len(podSpec.InitContainers, 1, "Should be one initContainer (the sidecar)") + sidecar := podSpec.InitContainers[0] + s.True(envContains(sidecar.Env, "TARGET_CONTAINER", "executor"), "Sidecar must have auto-injected TARGET_CONTAINER=executor") + s.True(volumeMountContains(sidecar.VolumeMounts, "telemetry-socket", "/tmp/telemetry"), "Sidecar must have auto-injected telemetry-socket volumeMount") + + executor := findContainer(podSpec.Containers, "executor") + s.Require().NotNil(executor, "executor container must exist") + s.True(volumeMountContains(executor.VolumeMounts, "telemetry-socket", "/tmp/telemetry"), "Executor must have auto-injected telemetry-socket volumeMount") + + s.True(volumeContains(podSpec.Volumes, "telemetry-socket"), "Pod must have auto-injected telemetry-socket emptyDir volume") + }, + }, + { + "PreservesExplicitTargetContainerOverride", + map[string]string{ + "delegatedOperatorJobTemplates.jobs.testJob.unused": "nil", + "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].name": "telemetry-sidecar", + "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].image": "telemetry-sidecar:latest", + "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].restartPolicy": "Always", + "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].env[0].name": "EXECUTOR_SIDECAR", + "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].env[0].value": "true", + "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].env[1].name": "TARGET_CONTAINER", + "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].env[1].value": "custom-executor", + }, + func(subT gruntworkTesting.TestingT, job batchv1.Job) { + sidecar := job.Spec.Template.Spec.InitContainers[0] + s.True(envContains(sidecar.Env, "TARGET_CONTAINER", "custom-executor"), "User-set TARGET_CONTAINER must win") + s.False(envContains(sidecar.Env, "TARGET_CONTAINER", "executor"), "Auto-inject must not also append TARGET_CONTAINER=executor") + }, + }, + { + "DoesNotInjectTelemetryWiringForNonExecutorSidecar", + map[string]string{ + "delegatedOperatorJobTemplates.jobs.testJob.unused": "nil", + "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].name": "istio-proxy", + "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].image": "istio-proxy:latest", + "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].restartPolicy": "Always", + }, + func(subT gruntworkTesting.TestingT, job batchv1.Job) { + podSpec := job.Spec.Template.Spec + s.False(volumeContains(podSpec.Volumes, "telemetry-socket"), "Non-executor sidecar must not auto-inject telemetry-socket volume") + sidecar := podSpec.InitContainers[0] + s.False(volumeMountContains(sidecar.VolumeMounts, "telemetry-socket", "/tmp/telemetry"), "Non-executor sidecar must not auto-inject volumeMount") + executor := findContainer(podSpec.Containers, "executor") + s.Require().NotNil(executor) + s.False(volumeMountContains(executor.VolumeMounts, "telemetry-socket", "/tmp/telemetry"), "Executor must not get telemetry-socket mount when no executor sidecar") + }, + }, + } + + for _, testCase := range testCases { + testCase := testCase + s.Run(testCase.name, func() { + subT := s.T() + subT.Parallel() + + options := &helm.Options{SetValues: testCase.values} + output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) + + var configMap corev1.ConfigMap + helm.UnmarshalK8SYaml(subT, output, &configMap) + + s.Require().NotEmpty(configMap.Data["testJob.yaml"], "testJob.yaml should be rendered in configmap data") + + jinjaArgs := map[string]interface{}{ + "_id": strings.ToLower(random.UniqueId()), + "_command": "fiftyone", + "_args": []string{"delegated", "launch"}, + } + actualJobYaml, err := convertJinjaToYAML(configMap.Data["testJob.yaml"], jinjaArgs) + s.NoError(err) + + var job batchv1.Job + helm.UnmarshalK8SYaml(subT, actualJobYaml, &job) + + testCase.expected(subT, job) + }) + } +} + +func envContains(envs []corev1.EnvVar, name, value string) bool { + for _, e := range envs { + if e.Name == name && e.Value == value { + return true + } + } + return false +} + +func volumeMountContains(vms []corev1.VolumeMount, name, mountPath string) bool { + for _, vm := range vms { + if vm.Name == name && vm.MountPath == mountPath { + return true + } + } + return false +} + +func volumeContains(vols []corev1.Volume, name string) bool { + for _, v := range vols { + if v.Name == name { + return true + } + } + return false +} + +func findContainer(containers []corev1.Container, name string) *corev1.Container { + for i, c := range containers { + if c.Name == name { + return &containers[i] + } + } + return nil +} From 37c46758604d1e8e6be441e5583116ee232af21f Mon Sep 17 00:00:00 2001 From: kacey Date: Mon, 18 May 2026 09:18:53 -0700 Subject: [PATCH 14/23] chore: attempt to fix test --- .../delegated-operator-job-configmap_test.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/unit/helm/delegated-operator-job-configmap_test.go b/tests/unit/helm/delegated-operator-job-configmap_test.go index 345bc2d4b..0199a13cc 100644 --- a/tests/unit/helm/delegated-operator-job-configmap_test.go +++ b/tests/unit/helm/delegated-operator-job-configmap_test.go @@ -660,9 +660,10 @@ func (s *doK8sConfigMapTemplateTest) loadTestFile(filename, chartVersion string) func (s *doK8sConfigMapTemplateTest) TestNativeSidecarTelemetryAutoInject() { testCases := []struct { - name string - values map[string]string - expected func(subT gruntworkTesting.TestingT, job batchv1.Job) + name string + values map[string]string + strValues map[string]string + expected func(subT gruntworkTesting.TestingT, job batchv1.Job) }{ { "AutoInjectsTelemetrySocketWiringWhenSidecarHasExecutorEnv", @@ -672,7 +673,9 @@ func (s *doK8sConfigMapTemplateTest) TestNativeSidecarTelemetryAutoInject() { "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].image": "telemetry-sidecar:latest", "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].restartPolicy": "Always", "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].env[0].name": "EXECUTOR_SIDECAR", - "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].env[0].value": "true", + }, + map[string]string{ + "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].env[0].value": "true", }, func(subT gruntworkTesting.TestingT, job batchv1.Job) { podSpec := job.Spec.Template.Spec @@ -700,10 +703,12 @@ func (s *doK8sConfigMapTemplateTest) TestNativeSidecarTelemetryAutoInject() { "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].image": "telemetry-sidecar:latest", "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].restartPolicy": "Always", "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].env[0].name": "EXECUTOR_SIDECAR", - "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].env[0].value": "true", "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].env[1].name": "TARGET_CONTAINER", "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].env[1].value": "custom-executor", }, + map[string]string{ + "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].env[0].value": "true", + }, func(subT gruntworkTesting.TestingT, job batchv1.Job) { sidecar := job.Spec.Template.Spec.InitContainers[0] s.True(envContains(sidecar.Env, "TARGET_CONTAINER", "custom-executor"), "User-set TARGET_CONTAINER must win") @@ -718,6 +723,7 @@ func (s *doK8sConfigMapTemplateTest) TestNativeSidecarTelemetryAutoInject() { "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].image": "istio-proxy:latest", "delegatedOperatorJobTemplates.template.nativeSidecarContainers[0].restartPolicy": "Always", }, + nil, func(subT gruntworkTesting.TestingT, job batchv1.Job) { podSpec := job.Spec.Template.Spec s.False(volumeContains(podSpec.Volumes, "telemetry-socket"), "Non-executor sidecar must not auto-inject telemetry-socket volume") @@ -736,7 +742,7 @@ func (s *doK8sConfigMapTemplateTest) TestNativeSidecarTelemetryAutoInject() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: testCase.values, SetStrValues: testCase.strValues} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var configMap corev1.ConfigMap From 0baf62e29e57fa06702ca50c972f883495a82386 Mon Sep 17 00:00:00 2001 From: kacey Date: Tue, 19 May 2026 08:34:28 -0700 Subject: [PATCH 15/23] feat: default true for telemetry and add templates --- helm/fiftyone-teams-app/README.md | 7 + .../templates/_telemetry.tpl | 40 ++++ .../delegated-operator-job-configmap.yaml | 4 + .../templates/telemetry-redis.yaml | 74 +++++++ .../templates/telemetry-rolebinding.yaml | 34 ++++ helm/fiftyone-teams-app/values.schema.json | 95 +++++++++ helm/fiftyone-teams-app/values.yaml | 37 ++++ tests/unit/helm/common_test.go | 4 +- tests/unit/helm/telemetry-redis_test.go | 190 ++++++++++++++++++ tests/unit/helm/telemetry-rolebinding_test.go | 182 +++++++++++++++++ tests/unit/helm/yaml_helpers.go | 17 ++ 11 files changed, 682 insertions(+), 2 deletions(-) create mode 100644 helm/fiftyone-teams-app/templates/_telemetry.tpl create mode 100644 helm/fiftyone-teams-app/templates/telemetry-redis.yaml create mode 100644 helm/fiftyone-teams-app/templates/telemetry-rolebinding.yaml create mode 100644 tests/unit/helm/telemetry-redis_test.go create mode 100644 tests/unit/helm/telemetry-rolebinding_test.go create mode 100644 tests/unit/helm/yaml_helpers.go diff --git a/helm/fiftyone-teams-app/README.md b/helm/fiftyone-teams-app/README.md index 391832585..a22049cd4 100644 --- a/helm/fiftyone-teams-app/README.md +++ b/helm/fiftyone-teams-app/README.md @@ -1094,6 +1094,13 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): | teamsAppSettings.updateStrategy | object | `{"type":"RollingUpdate"}` | Control how `teams-app` pods are redeployed during an upgrade. [Reference][upgrade-strategies] | | teamsAppSettings.volumeMounts | list | `[]` | Volume mounts for `teams-app` pods. [Reference][volumes]. | | teamsAppSettings.volumes | list | `[]` | Volumes for `teams-app` pods. [Reference][volumes]. | +| telemetry.enabled | bool | `true` | Whether to render the telemetry Redis Deployment/Service and the telemetry pod-logs Role/RoleBinding. Defaults to `true` so consumers that wire up `telemetry-sidecar` containers get a working redis without additional opt-in. Set to `false` to skip rendering all telemetry-related resources. | +| telemetry.redis.image | string | `"redis:7-alpine"` | Container image for the telemetry Redis Deployment. | +| telemetry.redis.maxmemory | string | `"400mb"` | `--maxmemory` flag passed to `redis-server`. | +| telemetry.redis.maxmemoryPolicy | string | `"allkeys-lru"` | `--maxmemory-policy` flag passed to `redis-server`. | +| telemetry.redis.resources | object | `{"limits":{"cpu":"250m","memory":"512Mi"},"requests":{"cpu":"100m","memory":"256Mi"}}` | Resource requests/limits for the telemetry Redis container. [Reference][resources]. | +| telemetry.redis.storage | string | `"1Gi"` | Persistent volume size for the telemetry Redis PVC. | +| telemetry.serviceAccounts | list | `[]` | ServiceAccount names (in `namespace.name`) bound to the telemetry pod-logs Role. Each entry becomes a `ServiceAccount` subject on the generated RoleBinding. When empty, the RoleBinding subjects default to the `default` ServiceAccount. | diff --git a/helm/fiftyone-teams-app/templates/_telemetry.tpl b/helm/fiftyone-teams-app/templates/_telemetry.tpl new file mode 100644 index 000000000..6ebe5248e --- /dev/null +++ b/helm/fiftyone-teams-app/templates/_telemetry.tpl @@ -0,0 +1,40 @@ +{{/* +Name of the telemetry redis Deployment/Service/PVC. +*/}} +{{- define "telemetry.redis.name" -}} +{{- printf "%s-telemetry-redis" .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- end }} + +{{/* +Selector labels for the telemetry redis Deployment/Service. +*/}} +{{- define "telemetry.redis.selectorLabels" -}} +app.kubernetes.io/name: telemetry-redis +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Combined labels for the telemetry redis Deployment/Service/PVC. +*/}} +{{- define "telemetry.redis.labels" -}} +{{- include "fiftyone-teams-app.commonLabels" . }} +{{ include "telemetry.redis.selectorLabels" . }} +app.voxel51.com/component: telemetry-redis +{{- end }} + +{{/* +Name of the telemetry Role and RoleBinding for the sidecar's pods/log access. +*/}} +{{- define "telemetry.role.name" -}} +{{- printf "%s-telemetry-pod-logs" .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- end }} + +{{/* +Combined labels for the telemetry Role and RoleBinding. +*/}} +{{- define "telemetry.role.labels" -}} +{{- include "fiftyone-teams-app.commonLabels" . }} +app.kubernetes.io/name: telemetry +app.kubernetes.io/instance: {{ .Release.Name }} +app.voxel51.com/component: telemetry +{{- end }} diff --git a/helm/fiftyone-teams-app/templates/delegated-operator-job-configmap.yaml b/helm/fiftyone-teams-app/templates/delegated-operator-job-configmap.yaml index b68661a8b..b611a83fd 100644 --- a/helm/fiftyone-teams-app/templates/delegated-operator-job-configmap.yaml +++ b/helm/fiftyone-teams-app/templates/delegated-operator-job-configmap.yaml @@ -172,6 +172,10 @@ data: {{- if not $hasSocketMount }} {{- $_ := set $rendered "volumeMounts" (append ($sidecar.volumeMounts | default list) (dict "name" "telemetry-socket" "mountPath" "/tmp/telemetry")) }} {{- end }} + {{- /* Gate the main container on the socket being bound — native sidecar readiness alone doesn't wait. */ -}} + {{- if not (hasKey $sidecar "readinessProbe") }} + {{- $_ := set $rendered "readinessProbe" (dict "exec" (dict "command" (list "sh" "-c" "test -S /tmp/telemetry/agent.sock")) "periodSeconds" 1 "failureThreshold" 30) }} + {{- end }} {{- end }} {{- list $rendered | toYaml | nindent 12 }} {{- end }} diff --git a/helm/fiftyone-teams-app/templates/telemetry-redis.yaml b/helm/fiftyone-teams-app/templates/telemetry-redis.yaml new file mode 100644 index 000000000..03c611da7 --- /dev/null +++ b/helm/fiftyone-teams-app/templates/telemetry-redis.yaml @@ -0,0 +1,74 @@ +{{- if .Values.telemetry.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "telemetry.redis.name" . }}-data + namespace: {{ .Values.namespace.name }} + labels: + {{- include "telemetry.redis.labels" . | nindent 4 }} +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.telemetry.redis.storage | default "1Gi" }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "telemetry.redis.name" . }} + namespace: {{ .Values.namespace.name }} + labels: + {{- include "telemetry.redis.labels" . | nindent 4 }} +spec: + replicas: 1 + selector: + matchLabels: + {{- include "telemetry.redis.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "telemetry.redis.labels" . | nindent 8 }} + spec: + containers: + - name: redis + image: {{ .Values.telemetry.redis.image | default "redis:7-alpine" }} + args: + - redis-server + - --save + - "60" + - "1" + - --dir + - /data + - --maxmemory + - {{ .Values.telemetry.redis.maxmemory | default "400mb" | quote }} + - --maxmemory-policy + - {{ .Values.telemetry.redis.maxmemoryPolicy | default "allkeys-lru" | quote }} + ports: + - containerPort: 6379 + {{- with .Values.telemetry.redis.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: redis-data + mountPath: /data + volumes: + - name: redis-data + persistentVolumeClaim: + claimName: {{ include "telemetry.redis.name" . }}-data +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "telemetry.redis.name" . }} + namespace: {{ .Values.namespace.name }} + labels: + {{- include "telemetry.redis.labels" . | nindent 4 }} +spec: + selector: + {{- include "telemetry.redis.selectorLabels" . | nindent 4 }} + ports: + - port: 6379 + targetPort: 6379 +{{- end }} diff --git a/helm/fiftyone-teams-app/templates/telemetry-rolebinding.yaml b/helm/fiftyone-teams-app/templates/telemetry-rolebinding.yaml new file mode 100644 index 000000000..e148ae7db --- /dev/null +++ b/helm/fiftyone-teams-app/templates/telemetry-rolebinding.yaml @@ -0,0 +1,34 @@ +{{- if .Values.telemetry.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "telemetry.role.name" . }} + namespace: {{ .Values.namespace.name }} + labels: + {{- include "telemetry.role.labels" . | nindent 4 }} +rules: + - apiGroups: [""] + resources: ["pods"] + verbs: ["get"] + - apiGroups: [""] + resources: ["pods/log"] + verbs: ["get"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "telemetry.role.name" . }} + namespace: {{ .Values.namespace.name }} + labels: + {{- include "telemetry.role.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "telemetry.role.name" . }} +subjects: +{{- range .Values.telemetry.serviceAccounts | default (list "default") }} + - kind: ServiceAccount + name: {{ . }} + namespace: {{ $.Values.namespace.name }} +{{- end }} +{{- end }} diff --git a/helm/fiftyone-teams-app/values.schema.json b/helm/fiftyone-teams-app/values.schema.json index 3f7b2b92a..ebcc62a3f 100644 --- a/helm/fiftyone-teams-app/values.schema.json +++ b/helm/fiftyone-teams-app/values.schema.json @@ -4008,6 +4008,101 @@ "required": [], "title": "teamsAppSettings", "type": "object" + }, + "telemetry": { + "description": "In-cluster telemetry support. When enabled, the chart renders a small\nRedis Deployment + Service (used by `telemetry-sidecar` containers\nconfigured on workload pods via `extraContainers`) plus a Role/RoleBinding\ngranting the sidecar's service accounts read access to `pods/log`.\nThe chart itself does NOT inject `telemetry-sidecar` containers; consumers\nwire those into `extraContainers` on the relevant workloads and point the\n`FIFTYONE_TELEMETRY_REDIS_URL` env var at this service.", + "properties": { + "enabled": { + "default": true, + "description": "Whether to render the telemetry Redis Deployment/Service and the\ntelemetry pod-logs Role/RoleBinding. Defaults to `true` so consumers\nthat wire up `telemetry-sidecar` containers get a working redis without\nadditional opt-in. Set to `false` to skip rendering all telemetry-related\nresources.", + "title": "enabled", + "type": "boolean" + }, + "redis": { + "properties": { + "image": { + "default": "redis:7-alpine", + "description": "Container image for the telemetry Redis Deployment.", + "title": "image", + "type": "string" + }, + "maxmemory": { + "default": "400mb", + "description": "`--maxmemory` flag passed to `redis-server`.", + "title": "maxmemory", + "type": "string" + }, + "maxmemoryPolicy": { + "default": "allkeys-lru", + "description": "`--maxmemory-policy` flag passed to `redis-server`.", + "title": "maxmemoryPolicy", + "type": "string" + }, + "resources": { + "description": "Resource requests/limits for the telemetry Redis container. [Reference][resources].", + "properties": { + "limits": { + "properties": { + "cpu": { + "default": "250m", + "title": "cpu", + "type": "string" + }, + "memory": { + "default": "512Mi", + "title": "memory", + "type": "string" + } + }, + "required": [], + "title": "limits", + "type": "object" + }, + "requests": { + "properties": { + "cpu": { + "default": "100m", + "title": "cpu", + "type": "string" + }, + "memory": { + "default": "256Mi", + "title": "memory", + "type": "string" + } + }, + "required": [], + "title": "requests", + "type": "object" + } + }, + "required": [], + "title": "resources", + "type": "object" + }, + "storage": { + "default": "1Gi", + "description": "Persistent volume size for the telemetry Redis PVC.", + "title": "storage", + "type": "string" + } + }, + "required": [], + "title": "redis", + "type": "object" + }, + "serviceAccounts": { + "description": "ServiceAccount names (in `namespace.name`) bound to the telemetry\npod-logs Role. Each entry becomes a `ServiceAccount` subject on the\ngenerated RoleBinding. When empty, the RoleBinding subjects default to\nthe `default` ServiceAccount.", + "items": { + "required": [] + }, + "title": "serviceAccounts", + "type": "array" + } + }, + "required": [], + "title": "telemetry", + "type": "object" } }, "required": [], diff --git a/helm/fiftyone-teams-app/values.yaml b/helm/fiftyone-teams-app/values.yaml index 5a8e18603..e5f65f64e 100644 --- a/helm/fiftyone-teams-app/values.yaml +++ b/helm/fiftyone-teams-app/values.yaml @@ -1642,3 +1642,40 @@ teamsAppSettings: volumeMounts: [] # -- Volumes for `teams-app` pods. [Reference][volumes]. volumes: [] + +# In-cluster telemetry support. When enabled, the chart renders a small +# Redis Deployment + Service (used by `telemetry-sidecar` containers +# configured on workload pods via `extraContainers`) plus a Role/RoleBinding +# granting the sidecar's service accounts read access to `pods/log`. +# The chart itself does NOT inject `telemetry-sidecar` containers; consumers +# wire those into `extraContainers` on the relevant workloads and point the +# `FIFTYONE_TELEMETRY_REDIS_URL` env var at this service. +telemetry: + # -- Whether to render the telemetry Redis Deployment/Service and the + # telemetry pod-logs Role/RoleBinding. Defaults to `true` so consumers + # that wire up `telemetry-sidecar` containers get a working redis without + # additional opt-in. Set to `false` to skip rendering all telemetry-related + # resources. + enabled: true + redis: + # -- Container image for the telemetry Redis Deployment. + image: redis:7-alpine + # -- Persistent volume size for the telemetry Redis PVC. + storage: 1Gi + # -- `--maxmemory` flag passed to `redis-server`. + maxmemory: 400mb + # -- `--maxmemory-policy` flag passed to `redis-server`. + maxmemoryPolicy: allkeys-lru + # -- Resource requests/limits for the telemetry Redis container. [Reference][resources]. + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 250m + memory: 512Mi + # -- ServiceAccount names (in `namespace.name`) bound to the telemetry + # pod-logs Role. Each entry becomes a `ServiceAccount` subject on the + # generated RoleBinding. When empty, the RoleBinding subjects default to + # the `default` ServiceAccount. + serviceAccounts: [] diff --git a/tests/unit/helm/common_test.go b/tests/unit/helm/common_test.go index 2ce0909b0..f4bf76ef0 100644 --- a/tests/unit/helm/common_test.go +++ b/tests/unit/helm/common_test.go @@ -1,5 +1,5 @@ -//go:build all || helm || unit || unitApiDeployment || unitApiService || unitAppDeployment || unitAppHpa || unitAppService || unitCasDeployment || unitCasService || unitIngress || unitNamespace || unitPluginsDeployment || unitHpaPlugins || unitPluginsService || unitSecrets || unitServiceAccount || unitTeamsAppDeployment || unitTeamsAppHpa || unitTeamsAppService -// +build all helm unit unitApiDeployment unitApiService unitAppDeployment unitAppHpa unitAppService unitCasDeployment unitCasService unitIngress unitNamespace unitPluginsDeployment unitHpaPlugins unitPluginsService unitSecrets unitServiceAccount unitTeamsAppDeployment unitTeamsAppHpa unitTeamsAppService +//go:build all || helm || unit || unitApiDeployment || unitApiService || unitAppDeployment || unitAppHpa || unitAppService || unitCasDeployment || unitCasService || unitIngress || unitNamespace || unitPluginsDeployment || unitHpaPlugins || unitPluginsService || unitSecrets || unitServiceAccount || unitTeamsAppDeployment || unitTeamsAppHpa || unitTeamsAppService || unitTelemetryRedis || unitTelemetryRoleBinding +// +build all helm unit unitApiDeployment unitApiService unitAppDeployment unitAppHpa unitAppService unitCasDeployment unitCasService unitIngress unitNamespace unitPluginsDeployment unitHpaPlugins unitPluginsService unitSecrets unitServiceAccount unitTeamsAppDeployment unitTeamsAppHpa unitTeamsAppService unitTelemetryRedis unitTelemetryRoleBinding package unit diff --git a/tests/unit/helm/telemetry-redis_test.go b/tests/unit/helm/telemetry-redis_test.go new file mode 100644 index 000000000..8ce69c431 --- /dev/null +++ b/tests/unit/helm/telemetry-redis_test.go @@ -0,0 +1,190 @@ +//go:build kubeall || helm || unit || unitTelemetryRedis +// +build kubeall helm unit unitTelemetryRedis + +package unit + +import ( + "fmt" + "path/filepath" + "strings" + "testing" + + "github.com/gruntwork-io/terratest/modules/helm" + "github.com/gruntwork-io/terratest/modules/random" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" +) + +type telemetryRedisTemplateTest struct { + suite.Suite + chartPath string + releaseName string + namespace string + templates []string +} + +func TestTelemetryRedisTemplate(t *testing.T) { + t.Parallel() + + helmChartPath, err := filepath.Abs(chartPath) + require.NoError(t, err) + + suite.Run(t, &telemetryRedisTemplateTest{ + Suite: suite.Suite{}, + chartPath: helmChartPath, + releaseName: "fiftyone-test", + namespace: "fiftyone-" + strings.ToLower(random.UniqueId()), + templates: []string{"templates/telemetry-redis.yaml"}, + }) +} + +func (s *telemetryRedisTemplateTest) TestEnabledByDefault() { + options := &helm.Options{SetValues: nil} + + output, err := helm.RenderTemplateE(s.T(), options, s.chartPath, s.releaseName, s.templates) + s.Require().NoError(err) + s.Contains(output, "kind: Deployment", "Redis Deployment should be rendered by default") + s.Contains(output, "kind: Service", "Redis Service should be rendered by default") + s.Contains(output, "kind: PersistentVolumeClaim", "Redis PVC should be rendered by default") +} + +func (s *telemetryRedisTemplateTest) TestExplicitlyDisabled() { + options := &helm.Options{SetValues: map[string]string{ + "telemetry.enabled": "false", + }} + + _, err := helm.RenderTemplateE(s.T(), options, s.chartPath, s.releaseName, s.templates) + s.ErrorContains(err, "could not find template templates/telemetry-redis.yaml in chart") +} + +// extractDeployment finds the Deployment document in the multi-doc render output. +func (s *telemetryRedisTemplateTest) extractDeployment(output string) appsv1.Deployment { + for _, doc := range splitYAMLDocs(output) { + if !strings.Contains(doc, "kind: Deployment") { + continue + } + var deployment appsv1.Deployment + helm.UnmarshalK8SYaml(s.T(), doc, &deployment) + return deployment + } + s.Fail("Deployment document not found in rendered output") + return appsv1.Deployment{} +} + +func (s *telemetryRedisTemplateTest) TestDeploymentMetadata() { + options := &helm.Options{SetValues: map[string]string{ + "telemetry.enabled": "true", + }} + + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, s.templates) + deployment := s.extractDeployment(output) + + expectedName := fmt.Sprintf("%s-telemetry-redis", s.releaseName) + s.Equal(expectedName, deployment.ObjectMeta.Name, "Deployment name should be release-prefixed") + s.Equal("fiftyone-teams", deployment.ObjectMeta.Namespace, "Deployment namespace should default to fiftyone-teams") + s.Equal("telemetry-redis", deployment.ObjectMeta.Labels["app.kubernetes.io/name"]) + s.Equal(s.releaseName, deployment.ObjectMeta.Labels["app.kubernetes.io/instance"]) + s.Equal("telemetry-redis", deployment.ObjectMeta.Labels["app.voxel51.com/component"]) +} + +func (s *telemetryRedisTemplateTest) TestDeploymentNamespaceOverride() { + options := &helm.Options{SetValues: map[string]string{ + "telemetry.enabled": "true", + "namespace.name": "my-ns", + }} + + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, s.templates) + deployment := s.extractDeployment(output) + s.Equal("my-ns", deployment.ObjectMeta.Namespace) +} + +func (s *telemetryRedisTemplateTest) TestDeploymentDefaultImage() { + options := &helm.Options{SetValues: map[string]string{ + "telemetry.enabled": "true", + }} + + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, s.templates) + deployment := s.extractDeployment(output) + + containers := deployment.Spec.Template.Spec.Containers + s.Require().Len(containers, 1, "Deployment should have exactly one container") + s.Equal("redis", containers[0].Name) + s.Equal("redis:7-alpine", containers[0].Image) +} + +func (s *telemetryRedisTemplateTest) TestDeploymentImageOverride() { + options := &helm.Options{SetValues: map[string]string{ + "telemetry.enabled": "true", + "telemetry.redis.image": "my-registry/redis:custom", + }} + + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, s.templates) + deployment := s.extractDeployment(output) + s.Equal("my-registry/redis:custom", deployment.Spec.Template.Spec.Containers[0].Image) +} + +func (s *telemetryRedisTemplateTest) TestDeploymentRedisArgs() { + options := &helm.Options{SetValues: map[string]string{ + "telemetry.enabled": "true", + "telemetry.redis.maxmemory": "1gb", + "telemetry.redis.maxmemoryPolicy": "noeviction", + }} + + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, s.templates) + deployment := s.extractDeployment(output) + + args := deployment.Spec.Template.Spec.Containers[0].Args + s.Contains(args, "redis-server") + s.Contains(args, "1gb", "maxmemory arg should be present") + s.Contains(args, "noeviction", "maxmemory-policy arg should be present") +} + +func (s *telemetryRedisTemplateTest) TestServiceMetadata() { + // The render output contains PVC + Deployment + Service. Use a Service-only + // unmarshal by isolating the Service doc. + options := &helm.Options{SetValues: map[string]string{ + "telemetry.enabled": "true", + }} + + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, s.templates) + + for _, doc := range splitYAMLDocs(output) { + if !strings.Contains(doc, "kind: Service") { + continue + } + var svc corev1.Service + helm.UnmarshalK8SYaml(s.T(), doc, &svc) + expectedName := fmt.Sprintf("%s-telemetry-redis", s.releaseName) + s.Equal(expectedName, svc.ObjectMeta.Name) + s.Require().Len(svc.Spec.Ports, 1) + s.EqualValues(6379, svc.Spec.Ports[0].Port) + return + } + s.Fail("Service document not found in rendered output") +} + +func (s *telemetryRedisTemplateTest) TestPVCMetadata() { + options := &helm.Options{SetValues: map[string]string{ + "telemetry.enabled": "true", + "telemetry.redis.storage": "5Gi", + }} + + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, s.templates) + + for _, doc := range splitYAMLDocs(output) { + if !strings.Contains(doc, "kind: PersistentVolumeClaim") { + continue + } + var pvc corev1.PersistentVolumeClaim + helm.UnmarshalK8SYaml(s.T(), doc, &pvc) + expectedName := fmt.Sprintf("%s-telemetry-redis-data", s.releaseName) + s.Equal(expectedName, pvc.ObjectMeta.Name) + req := pvc.Spec.Resources.Requests[corev1.ResourceStorage] + s.Equal("5Gi", req.String()) + return + } + s.Fail("PVC document not found in rendered output") +} diff --git a/tests/unit/helm/telemetry-rolebinding_test.go b/tests/unit/helm/telemetry-rolebinding_test.go new file mode 100644 index 000000000..7b052daf4 --- /dev/null +++ b/tests/unit/helm/telemetry-rolebinding_test.go @@ -0,0 +1,182 @@ +//go:build kubeall || helm || unit || unitTelemetryRoleBinding +// +build kubeall helm unit unitTelemetryRoleBinding + +package unit + +import ( + "fmt" + "path/filepath" + "strings" + "testing" + + "github.com/gruntwork-io/terratest/modules/helm" + "github.com/gruntwork-io/terratest/modules/random" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + rbacv1 "k8s.io/api/rbac/v1" +) + +type telemetryRoleBindingTemplateTest struct { + suite.Suite + chartPath string + releaseName string + namespace string + templates []string +} + +func TestTelemetryRoleBindingTemplate(t *testing.T) { + t.Parallel() + + helmChartPath, err := filepath.Abs(chartPath) + require.NoError(t, err) + + suite.Run(t, &telemetryRoleBindingTemplateTest{ + Suite: suite.Suite{}, + chartPath: helmChartPath, + releaseName: "fiftyone-test", + namespace: "fiftyone-" + strings.ToLower(random.UniqueId()), + templates: []string{"templates/telemetry-rolebinding.yaml"}, + }) +} + +func (s *telemetryRoleBindingTemplateTest) TestEnabledByDefault() { + options := &helm.Options{SetValues: nil} + + output, err := helm.RenderTemplateE(s.T(), options, s.chartPath, s.releaseName, s.templates) + s.Require().NoError(err) + s.Contains(output, "kind: Role", "Role should be rendered by default") + s.Contains(output, "kind: RoleBinding", "RoleBinding should be rendered by default") +} + +func (s *telemetryRoleBindingTemplateTest) TestExplicitlyDisabled() { + options := &helm.Options{SetValues: map[string]string{ + "telemetry.enabled": "false", + }} + + _, err := helm.RenderTemplateE(s.T(), options, s.chartPath, s.releaseName, s.templates) + s.ErrorContains(err, "could not find template templates/telemetry-rolebinding.yaml in chart") +} + +// extractRole finds the Role document (not RoleBinding) in multi-doc render output. +func (s *telemetryRoleBindingTemplateTest) extractRole(output string) (rbacv1.Role, bool) { + for _, doc := range splitYAMLDocs(output) { + if !strings.Contains(doc, "\nkind: Role\n") { + continue + } + var role rbacv1.Role + helm.UnmarshalK8SYaml(s.T(), doc, &role) + return role, true + } + return rbacv1.Role{}, false +} + +// extractRoleBinding finds the RoleBinding document in multi-doc render output. +func (s *telemetryRoleBindingTemplateTest) extractRoleBinding(output string) (rbacv1.RoleBinding, bool) { + for _, doc := range splitYAMLDocs(output) { + if !strings.Contains(doc, "kind: RoleBinding") { + continue + } + var rb rbacv1.RoleBinding + helm.UnmarshalK8SYaml(s.T(), doc, &rb) + return rb, true + } + return rbacv1.RoleBinding{}, false +} + +func (s *telemetryRoleBindingTemplateTest) TestRoleMetadata() { + options := &helm.Options{SetValues: map[string]string{ + "telemetry.enabled": "true", + }} + + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, s.templates) + role, ok := s.extractRole(output) + s.Require().True(ok, "Role document not found in rendered output") + + expectedName := fmt.Sprintf("%s-telemetry-pod-logs", s.releaseName) + s.Equal(expectedName, role.ObjectMeta.Name) + s.Equal("fiftyone-teams", role.ObjectMeta.Namespace) + s.Equal("telemetry", role.ObjectMeta.Labels["app.kubernetes.io/name"]) + s.Equal(s.releaseName, role.ObjectMeta.Labels["app.kubernetes.io/instance"]) + s.Equal("telemetry", role.ObjectMeta.Labels["app.voxel51.com/component"]) +} + +func (s *telemetryRoleBindingTemplateTest) TestRoleRules() { + options := &helm.Options{SetValues: map[string]string{ + "telemetry.enabled": "true", + }} + + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, s.templates) + role, ok := s.extractRole(output) + s.Require().True(ok, "Role document not found in rendered output") + + s.Require().Len(role.Rules, 2, "Role should have exactly two rules") + + var hasPodsGet, hasPodsLogGet bool + for _, rule := range role.Rules { + for _, resource := range rule.Resources { + if resource == "pods" { + s.Contains(rule.Verbs, "get", "pods rule should grant get") + hasPodsGet = true + } + if resource == "pods/log" { + s.Contains(rule.Verbs, "get", "pods/log rule should grant get") + hasPodsLogGet = true + } + } + } + s.True(hasPodsGet, "Role should grant get on pods") + s.True(hasPodsLogGet, "Role should grant get on pods/log") +} + +func (s *telemetryRoleBindingTemplateTest) TestRoleBindingDefaultSubject() { + // With an unset serviceAccounts list, the binding should fall back to + // a single subject pointing at the "default" ServiceAccount in the + // chart namespace. + options := &helm.Options{SetValues: map[string]string{ + "telemetry.enabled": "true", + }} + + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, s.templates) + rb, ok := s.extractRoleBinding(output) + s.Require().True(ok, "RoleBinding document not found in rendered output") + + s.Require().Len(rb.Subjects, 1, "Default RoleBinding should have exactly one subject") + s.Equal("ServiceAccount", rb.Subjects[0].Kind) + s.Equal("default", rb.Subjects[0].Name) + s.Equal("fiftyone-teams", rb.Subjects[0].Namespace) +} + +func (s *telemetryRoleBindingTemplateTest) TestRoleBindingMultipleSubjects() { + options := &helm.Options{SetValues: map[string]string{ + "telemetry.enabled": "true", + "telemetry.serviceAccounts[0]": "sa-one", + "telemetry.serviceAccounts[1]": "sa-two", + "namespace.name": "my-ns", + }} + + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, s.templates) + rb, ok := s.extractRoleBinding(output) + s.Require().True(ok, "RoleBinding document not found in rendered output") + + s.Require().Len(rb.Subjects, 2) + s.Equal("sa-one", rb.Subjects[0].Name) + s.Equal("my-ns", rb.Subjects[0].Namespace) + s.Equal("sa-two", rb.Subjects[1].Name) + s.Equal("my-ns", rb.Subjects[1].Namespace) +} + +func (s *telemetryRoleBindingTemplateTest) TestRoleBindingRoleRef() { + options := &helm.Options{SetValues: map[string]string{ + "telemetry.enabled": "true", + }} + + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, s.templates) + rb, ok := s.extractRoleBinding(output) + s.Require().True(ok, "RoleBinding document not found in rendered output") + + expectedName := fmt.Sprintf("%s-telemetry-pod-logs", s.releaseName) + s.Equal("Role", rb.RoleRef.Kind) + s.Equal(expectedName, rb.RoleRef.Name) + s.Equal("rbac.authorization.k8s.io", rb.RoleRef.APIGroup) +} diff --git a/tests/unit/helm/yaml_helpers.go b/tests/unit/helm/yaml_helpers.go new file mode 100644 index 000000000..05e13c0e7 --- /dev/null +++ b/tests/unit/helm/yaml_helpers.go @@ -0,0 +1,17 @@ +package unit + +import "strings" + +// splitYAMLDocs splits a multi-doc YAML string on `---` separators. +// Empty/whitespace-only docs are dropped. +func splitYAMLDocs(s string) []string { + out := []string{} + for _, raw := range strings.Split(s, "\n---") { + doc := strings.TrimSpace(raw) + if doc == "" { + continue + } + out = append(out, doc) + } + return out +} From d00c990a8aaa53e4f0093bf893d991eb2f5c5a76 Mon Sep 17 00:00:00 2001 From: kacey Date: Tue, 19 May 2026 09:37:13 -0700 Subject: [PATCH 16/23] feat: enable sidecar by default in docker deployments --- docker/README.md | 8 +- docker/common-services.yaml | 13 +- docker/docs/configuring-telemetry.md | 87 +++++++---- .../compose.dedicated-plugins.yaml | 75 ++++++++++ .../compose.delegated-operators.gpu.yaml | 5 +- .../compose.delegated-operators.yaml | 34 +++++ docker/internal-auth/compose.plugins.yaml | 56 +++++++ ...compose.telemetry.delegated-operators.yaml | 62 -------- .../compose.telemetry.plugins.yaml | 37 ----- docker/internal-auth/compose.telemetry.yaml | 90 ----------- docker/internal-auth/compose.yaml | 66 ++++++++ .../compose.dedicated-plugins.yaml | 75 ++++++++++ .../compose.delegated-operators.gpu.yaml | 5 +- .../compose.delegated-operators.yaml | 34 +++++ docker/legacy-auth/compose.plugins.yaml | 56 +++++++ ...compose.telemetry.delegated-operators.yaml | 62 -------- .../compose.telemetry.plugins.yaml | 37 ----- docker/legacy-auth/compose.telemetry.yaml | 90 ----------- docker/legacy-auth/compose.yaml | 66 ++++++++ .../configuring-kubernetes-orchestrator.md | 12 +- .../docker-compose-internal-auth_test.go | 138 +++++++++-------- .../docker-compose-legacy-auth_test.go | 141 +++++++++--------- utils/validate-docker-pulls.sh | 1 + 23 files changed, 685 insertions(+), 565 deletions(-) delete mode 100644 docker/internal-auth/compose.telemetry.delegated-operators.yaml delete mode 100644 docker/internal-auth/compose.telemetry.plugins.yaml delete mode 100644 docker/internal-auth/compose.telemetry.yaml delete mode 100644 docker/legacy-auth/compose.telemetry.delegated-operators.yaml delete mode 100644 docker/legacy-auth/compose.telemetry.plugins.yaml delete mode 100644 docker/legacy-auth/compose.telemetry.yaml diff --git a/docker/README.md b/docker/README.md index 2417e1100..88272dbf8 100644 --- a/docker/README.md +++ b/docker/README.md @@ -659,10 +659,10 @@ for questions regarding storage and encryption. ### Telemetry -FiftyOne Enterprise ships an optional telemetry overlay that exposes live -per-service metrics (CPU, memory, FDs, thread counts) and tailed logs on -the Settings → Metrics page. Enable by layering `compose.telemetry.yaml` -onto your existing compose invocation. +FiftyOne Enterprise bundles a telemetry sidecar + Redis backend by +default in every compose file. The Settings → Metrics page in teams-app +exposes live per-service metrics (CPU, memory, FDs, thread counts) and +tailed logs. No opt-in flags are required. Please refer to the [telemetry configuration documentation](./docs/configuring-telemetry.md) diff --git a/docker/common-services.yaml b/docker/common-services.yaml index 7050831db..75a0b81f1 100644 --- a/docker/common-services.yaml +++ b/docker/common-services.yaml @@ -17,6 +17,7 @@ services: FIFTYONE_MEDIA_CACHE_APP_IMAGES: false FIFTYONE_MEDIA_CACHE_SIZE_BYTES: -1 FIFTYONE_SIGNED_URL_EXPIRATION: ${FIFTYONE_SIGNED_URL_EXPIRATION:-24} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} # If you are routing through a proxy server you will want to set # HTTP_PROXY_URL, HTTPS_PROXY_URL, and NO_PROXY_LIST in your .env # then add the following environment variables to your @@ -43,6 +44,7 @@ services: FIFTYONE_ENV: ${FIFTYONE_ENV} FIFTYONE_INTERNAL_SERVICE: true FIFTYONE_LOGGING_FORMAT: ${FIFTYONE_LOGGING_FORMAT:-text} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} GRAPHQL_DEFAULT_LIMIT: ${GRAPHQL_DEFAULT_LIMIT} LOGGING_LEVEL: ${API_LOGGING_LEVEL:-INFO} MONGO_DEFAULT_DB: ${FIFTYONE_DATABASE_NAME} @@ -72,6 +74,7 @@ services: FIFTYONE_SERVER_ADDRESS: "" FIFTYONE_SERVER_PATH_PREFIX: /api/proxy/fiftyone-teams FIFTYONE_TEAMS_PROXY_URL: ${FIFTYONE_TEAMS_PROXY_URL} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} NODE_ENV: production RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED: false FIFTYONE_APP_ANONYMOUS_ANALYTICS_ENABLED: ${FIFTYONE_APP_ANONYMOUS_ANALYTICS_ENABLED:-true} @@ -144,6 +147,7 @@ services: FIFTYONE_MEDIA_CACHE_APP_IMAGES: false FIFTYONE_MEDIA_CACHE_SIZE_BYTES: -1 FIFTYONE_PLUGINS_DIR: /opt/plugins + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} # If you are routing through a proxy server you will want to set # HTTP_PROXY_URL, HTTPS_PROXY_URL, and NO_PROXY_LIST in your .env # then add the following environment variables to your @@ -158,8 +162,12 @@ services: teams-do-common: image: voxel51/fiftyone-teams-cv-full:v2.18.1 + # Telemetry default-on requires teams-do to share its PID namespace + # with a single sidecar (compose's `pid: "service:"` only joins + # one replica). Force replicas=1 to keep the 1:1 pairing deterministic. + # See docker/docs/configuring-telemetry.md for multi-worker patterns. deploy: - replicas: ${FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS:-3} + replicas: 1 command: > /bin/sh -c "fiftyone delegated launch -t remote -m" environment: @@ -171,6 +179,9 @@ services: FIFTYONE_INTERNAL_SERVICE: true FIFTYONE_MEDIA_CACHE_SIZE_BYTES: -1 FIFTYONE_PLUGINS_DIR: /opt/plugins + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + TELEMETRY_SOCKET: /tmp/telemetry/agent.sock restart: always volumes: - plugins-vol:/opt/plugins:ro + - telemetry-socket:/tmp/telemetry diff --git a/docker/docs/configuring-telemetry.md b/docker/docs/configuring-telemetry.md index 0ecadcdac..99f7d0bb1 100644 --- a/docker/docs/configuring-telemetry.md +++ b/docker/docs/configuring-telemetry.md @@ -14,39 +14,38 @@ # Configuring FiftyOne Enterprise Telemetry -The telemetry overlay adds a lightweight per-service metrics collector -(sidecar pattern) plus a Redis backend. Once enabled, the Settings → -Metrics page in teams-app displays live CPU / memory / thread / file-descriptor -samples and tailed stdout logs for each observed service. +Telemetry adds a lightweight per-service metrics collector (sidecar +pattern) plus a Redis backend. The Settings → Metrics page in teams-app +displays live CPU / memory / thread / file-descriptor samples and tailed +stdout logs for each observed service. -This overlay is optional and layered on top of the main compose file. +**Telemetry is enabled by default.** The base compose files bundle the +`telemetry-redis` service and per-workload sidecars; no opt-in flags or +overlay files are needed. -## Enable - -Use the `compose.telemetry.yaml` overlay alongside your normal deployment: +## Default deployment ```shell -docker compose \ - -f compose.yaml \ - -f compose.telemetry.yaml \ - up -d +docker compose -f compose.yaml up -d ``` -Can be combined with the plugins or delegated-operators overlays. Each -optional service has its own companion telemetry overlay so its sidecar -is added only when that service is enabled: +renders `fiftyone-app`, `teams-api`, `teams-app`, `teams-cas`, +`telemetry-redis`, `fiftyone-app-telemetry`, and `teams-api-telemetry`. + +Combine with optional overlays as before — each carries its own bundled +sidecar: ```shell docker compose \ -f compose.yaml \ -f compose.dedicated-plugins.yaml \ -f compose.delegated-operators.yaml \ - -f compose.telemetry.yaml \ - -f compose.telemetry.plugins.yaml \ - -f compose.telemetry.delegated-operators.yaml \ up -d ``` +This adds `teams-plugins`, `teams-plugins-telemetry`, `teams-do`, and +`teams-do-telemetry` in addition to the default set. + For GPU-enabled delegated operators, layer `compose.delegated-operators.gpu.yaml` (which includes its own bundled telemetry sidecar) and activate the `gpu` profile: @@ -56,12 +55,10 @@ docker compose --profile gpu \ -f compose.yaml \ -f compose.delegated-operators.yaml \ -f compose.delegated-operators.gpu.yaml \ - -f compose.telemetry.yaml \ - -f compose.telemetry.delegated-operators.yaml \ up -d ``` -## What it adds +## What's bundled by default - `telemetry-redis` — Redis 7 container that holds metric streams and log entries. Data is capped by a maxmemory policy (`allkeys-lru`) so disk usage @@ -71,11 +68,10 @@ docker compose --profile gpu \ `pid: "service:"` so it can read `/proc//fd/1` and use psutil to sample CPU, memory, FDs, and thread counts. Sidecars run with the `SYS_PTRACE` capability so py-spy can attach to the target. -- `teams-plugins-telemetry` (only with `compose.telemetry.plugins.yaml`) — +- `teams-plugins-telemetry` (only with `compose.dedicated-plugins.yaml`) — sidecar for the dedicated `teams-plugins` service. -- `teams-do-telemetry` (only with - `compose.telemetry.delegated-operators.yaml`) — sidecar in - `EXECUTOR_SIDECAR=true` mode that watches the executor for +- `teams-do-telemetry` (only with `compose.delegated-operators.yaml`) — + sidecar in `EXECUTOR_SIDECAR=true` mode that watches the executor for per-operation child processes and records per-op metrics back to the `delegated_ops` MongoDB document. - `teams-do-gpu` + `teams-do-gpu-telemetry` (only with @@ -84,15 +80,46 @@ docker compose --profile gpu \ orchestrator (`-n teams-do-gpu`) plus its paired sidecar. Sidecar reads GPU metrics via NVML and requires its own GPU reservation. - `FIFTYONE_TELEMETRY_REDIS_URL` injected on `fiftyone-app`, `teams-api`, - `teams-app`, and (when the DO overlay is used) `teams-do` so the in-app - telemetry blueprint and SSE endpoints can read from Redis. + `teams-app`, `teams-plugins`, and (when the DO overlay is used) + `teams-do` so the in-app telemetry blueprint and SSE endpoints can + read from Redis. + +## Opt out + +To run without telemetry, add a `compose.override.yaml` that scales the +telemetry services to zero replicas: + +```yaml +services: + telemetry-redis: + deploy: + replicas: 0 + fiftyone-app-telemetry: + deploy: + replicas: 0 + teams-api-telemetry: + deploy: + replicas: 0 + # Only needed when running the corresponding overlay: + teams-plugins-telemetry: + deploy: + replicas: 0 + teams-do-telemetry: + deploy: + replicas: 0 +``` + +`docker compose -f compose.yaml -f compose.override.yaml up -d` will start +the base services without the telemetry collector. The main containers +will still have `FIFTYONE_TELEMETRY_REDIS_URL` set, but the in-app agent +gracefully no-ops when Redis is unreachable. ### Scaling teams-do with telemetry docker-compose's `pid: "service:"` only joins a single replica's -PID namespace. To keep the sidecar observation honest, the telemetry DO -overlay **forces `teams-do` replicas to 1**, overriding -`FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS`. +PID namespace. To keep the sidecar observation honest, `teams-do-common` +**forces `teams-do` replicas to 1**, overriding any +`FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS` setting. If you need more than one delegated-operator worker observed at the same time, either: diff --git a/docker/internal-auth/compose.dedicated-plugins.yaml b/docker/internal-auth/compose.dedicated-plugins.yaml index 1cf90e790..e037e6ef0 100644 --- a/docker/internal-auth/compose.dedicated-plugins.yaml +++ b/docker/internal-auth/compose.dedicated-plugins.yaml @@ -39,5 +39,80 @@ services: volumes: - plugins-vol:/opt/plugins:ro + telemetry-redis: + image: ${TELEMETRY_REDIS_IMAGE:-redis:7-alpine} + command: + - redis-server + - --save + - "60" + - "1" + - --dir + - /data + - --maxmemory + - ${TELEMETRY_REDIS_MAXMEMORY:-400mb} + - --maxmemory-policy + - allkeys-lru + restart: always + volumes: + - telemetry-redis-data:/data + + fiftyone-app-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:fiftyone-app" + cap_add: + - SYS_PTRACE + environment: + POD_NAME: fiftyone-app + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: fiftyone-app + TARGET_NAME: ${FIFTYONE_APP_TARGET_NAME:-hypercorn} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + depends_on: + fiftyone-app: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + + teams-api-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:teams-api" + cap_add: + - SYS_PTRACE + environment: + POD_NAME: teams-api + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: teams-api + TARGET_NAME: ${TEAMS_API_TARGET_NAME:-fiftyone-teams-api} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + depends_on: + teams-api: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + + teams-plugins-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:teams-plugins" + cap_add: + - SYS_PTRACE + environment: + POD_NAME: teams-plugins + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: teams-plugins + TARGET_NAME: ${TEAMS_PLUGINS_TARGET_NAME:-hypercorn} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + depends_on: + teams-plugins: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + volumes: plugins-vol: + telemetry-redis-data: diff --git a/docker/internal-auth/compose.delegated-operators.gpu.yaml b/docker/internal-auth/compose.delegated-operators.gpu.yaml index 6f38f07bb..a6de978d8 100644 --- a/docker/internal-auth/compose.delegated-operators.gpu.yaml +++ b/docker/internal-auth/compose.delegated-operators.gpu.yaml @@ -10,10 +10,11 @@ # -f compose.yaml \ # -f compose.delegated-operators.yaml \ # -f compose.delegated-operators.gpu.yaml \ -# -f compose.telemetry.yaml \ -# -f compose.telemetry.delegated-operators.yaml \ # up -d # +# Telemetry (redis + paired sidecars) is bundled by default in the base +# compose.yaml and compose.delegated-operators.yaml files. +# # For Proxy Server instructions please see # https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies services: diff --git a/docker/internal-auth/compose.delegated-operators.yaml b/docker/internal-auth/compose.delegated-operators.yaml index 5337711f8..2daddc633 100644 --- a/docker/internal-auth/compose.delegated-operators.yaml +++ b/docker/internal-auth/compose.delegated-operators.yaml @@ -1,4 +1,12 @@ --- +# Layered on top of a base compose file (e.g. compose.yaml). The base +# compose file is expected to provide the `telemetry-redis` service. +# +# `teams-do` is forced to `replicas: 1` (see teams-do-common in +# ../common-services.yaml) so the paired `teams-do-telemetry` sidecar's +# `pid: "service:teams-do"` joins a single deterministic PID namespace. +# For multi-worker patterns see docker/docs/configuring-telemetry.md. +# # For Proxy Server instructions please see # https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies services: @@ -7,5 +15,31 @@ services: file: ../common-services.yaml service: teams-do-common + teams-do-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:teams-do" + cap_add: + - SYS_PTRACE + environment: + POD_NAME: teams-do + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: delegated-operator + TARGET_NAME: ${TEAMS_DO_TARGET_NAME:-fiftyone delegated} + EXECUTOR_SIDECAR: "true" + TELEMETRY_SOCKET: /tmp/telemetry/agent.sock + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + FIFTYONE_DATABASE_URI: ${FIFTYONE_DATABASE_URI} + FIFTYONE_DATABASE_NAME: ${FIFTYONE_DATABASE_NAME} + volumes: + - telemetry-socket:/tmp/telemetry + depends_on: + teams-do: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + volumes: plugins-vol: + telemetry-socket: diff --git a/docker/internal-auth/compose.plugins.yaml b/docker/internal-auth/compose.plugins.yaml index 1eefc9b95..66fb916a4 100644 --- a/docker/internal-auth/compose.plugins.yaml +++ b/docker/internal-auth/compose.plugins.yaml @@ -34,5 +34,61 @@ services: file: ../common-services.yaml service: teams-cas-common + telemetry-redis: + image: ${TELEMETRY_REDIS_IMAGE:-redis:7-alpine} + command: + - redis-server + - --save + - "60" + - "1" + - --dir + - /data + - --maxmemory + - ${TELEMETRY_REDIS_MAXMEMORY:-400mb} + - --maxmemory-policy + - allkeys-lru + restart: always + volumes: + - telemetry-redis-data:/data + + fiftyone-app-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:fiftyone-app" + cap_add: + - SYS_PTRACE + environment: + POD_NAME: fiftyone-app + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: fiftyone-app + TARGET_NAME: ${FIFTYONE_APP_TARGET_NAME:-hypercorn} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + depends_on: + fiftyone-app: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + + teams-api-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:teams-api" + cap_add: + - SYS_PTRACE + environment: + POD_NAME: teams-api + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: teams-api + TARGET_NAME: ${TEAMS_API_TARGET_NAME:-fiftyone-teams-api} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + depends_on: + teams-api: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + volumes: plugins-vol: + telemetry-redis-data: diff --git a/docker/internal-auth/compose.telemetry.delegated-operators.yaml b/docker/internal-auth/compose.telemetry.delegated-operators.yaml deleted file mode 100644 index f6a1c7d51..000000000 --- a/docker/internal-auth/compose.telemetry.delegated-operators.yaml +++ /dev/null @@ -1,62 +0,0 @@ ---- -# Telemetry overlay for the delegated operators (teams-do) service. -# Use alongside compose.telemetry.yaml and compose.delegated-operators.yaml: -# -# docker compose -f compose.yaml \ -# -f compose.delegated-operators.yaml \ -# -f compose.telemetry.yaml \ -# -f compose.telemetry.delegated-operators.yaml \ -# up -d -# -# The EXECUTOR_SIDECAR mode watches the teams-do executor for per-operation -# child processes and records per-op metrics back to the delegated_ops -# MongoDB document (via FIFTYONE_DATABASE_URI). -# -# This overlay FORCES `teams-do` replicas to 1 so the sidecar has a -# deterministic 1:1 pairing via the shared PID namespace. docker-compose's -# `pid: "service:"` only joins a single replica's PID namespace, -# so running multiple replicas with a single sidecar would silently miss -# the other replicas. If you need multiple DO workers observed, run one -# teams-do + sidecar pair per worker by defining additional services -# (teams-do-1, teams-do-2, ...), or switch to the helm chart which scales -# the sidecar alongside each pod automatically. -# -# For Proxy Server instructions please see -# https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies -services: - teams-do: - environment: - FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} - TELEMETRY_SOCKET: /tmp/telemetry/agent.sock - deploy: - replicas: 1 - volumes: - - telemetry-socket:/tmp/telemetry - - teams-do-telemetry: - image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} - pid: "service:teams-do" - cap_add: - - SYS_PTRACE - environment: - POD_NAME: teams-do - POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} - SERVICE_TYPE: delegated-operator - TARGET_NAME: ${TEAMS_DO_TARGET_NAME:-fiftyone delegated} - EXECUTOR_SIDECAR: "true" - TELEMETRY_SOCKET: /tmp/telemetry/agent.sock - FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} - FIFTYONE_DATABASE_URI: ${FIFTYONE_DATABASE_URI} - FIFTYONE_DATABASE_NAME: ${FIFTYONE_DATABASE_NAME} - volumes: - - telemetry-socket:/tmp/telemetry - depends_on: - teams-do: - condition: service_started - restart: true - telemetry-redis: - condition: service_started - restart: always - -volumes: - telemetry-socket: diff --git a/docker/internal-auth/compose.telemetry.plugins.yaml b/docker/internal-auth/compose.telemetry.plugins.yaml deleted file mode 100644 index ac1de8bf2..000000000 --- a/docker/internal-auth/compose.telemetry.plugins.yaml +++ /dev/null @@ -1,37 +0,0 @@ ---- -# Telemetry overlay for the dedicated teams-plugins service. The sidecar -# shares the target's PID namespace via `pid: "service:teams-plugins"`. -# -# Layered after compose.dedicated-plugins.yaml + compose.telemetry.yaml: -# -# docker compose -f compose.yaml \ -# -f compose.dedicated-plugins.yaml \ -# -f compose.telemetry.yaml \ -# -f compose.telemetry.plugins.yaml \ -# up -d -# -# For Proxy Server instructions please see -# https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies -services: - teams-plugins: - environment: - FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} - - teams-plugins-telemetry: - image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} - pid: "service:teams-plugins" - cap_add: - - SYS_PTRACE - environment: - POD_NAME: teams-plugins - POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} - SERVICE_TYPE: teams-plugins - TARGET_NAME: ${TEAMS_PLUGINS_TARGET_NAME:-hypercorn} - FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} - depends_on: - teams-plugins: - condition: service_started - restart: true - telemetry-redis: - condition: service_started - restart: always diff --git a/docker/internal-auth/compose.telemetry.yaml b/docker/internal-auth/compose.telemetry.yaml deleted file mode 100644 index 89b9d57a4..000000000 --- a/docker/internal-auth/compose.telemetry.yaml +++ /dev/null @@ -1,90 +0,0 @@ ---- -# Telemetry overlay. Adds a Redis backend + sidecar containers that observe -# each target service via a shared PID namespace, publishing metrics and -# log events to Redis. -# -# Layered on top of the main compose file, e.g.: -# docker compose -f compose.yaml -f compose.telemetry.yaml up -# -# Or with delegated operators and/or dedicated plugins: -# docker compose -f compose.yaml \ -# -f compose.delegated-operators.yaml \ -# -f compose.dedicated-plugins.yaml \ -# -f compose.telemetry.yaml up -# -# For Proxy Server instructions please see -# https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies -services: - telemetry-redis: - image: ${TELEMETRY_REDIS_IMAGE:-redis:7-alpine} - command: - - redis-server - - --save - - "60" - - "1" - - --dir - - /data - - --maxmemory - - ${TELEMETRY_REDIS_MAXMEMORY:-400mb} - - --maxmemory-policy - - allkeys-lru - restart: always - volumes: - - telemetry-redis-data:/data - - fiftyone-app: - environment: - FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} - - teams-api: - environment: - FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} - - teams-app: - environment: - FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} - - # One sidecar per observed service. `pid: "service:"` joins the - # target's PID namespace so the sidecar can read /proc//fd/1 and - # psutil can see the target's process. - - fiftyone-app-telemetry: - image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} - pid: "service:fiftyone-app" - cap_add: - - SYS_PTRACE - environment: - POD_NAME: fiftyone-app - POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} - SERVICE_TYPE: fiftyone-app - TARGET_NAME: ${FIFTYONE_APP_TARGET_NAME:-hypercorn} - FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} - depends_on: - fiftyone-app: - condition: service_started - restart: true - telemetry-redis: - condition: service_started - restart: always - - teams-api-telemetry: - image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} - pid: "service:teams-api" - cap_add: - - SYS_PTRACE - environment: - POD_NAME: teams-api - POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} - SERVICE_TYPE: teams-api - TARGET_NAME: ${TEAMS_API_TARGET_NAME:-fiftyone-teams-api} - FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} - depends_on: - teams-api: - condition: service_started - restart: true - telemetry-redis: - condition: service_started - restart: always - -volumes: - telemetry-redis-data: diff --git a/docker/internal-auth/compose.yaml b/docker/internal-auth/compose.yaml index 896585019..b2a9a83fd 100644 --- a/docker/internal-auth/compose.yaml +++ b/docker/internal-auth/compose.yaml @@ -1,6 +1,11 @@ --- # For Proxy Server instructions please see # https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies +# +# Telemetry (`telemetry-redis` + per-service sidecars) is enabled by +# default. To opt out, scale the telemetry services to 0 in a +# `compose.override.yaml`, or unset `FIFTYONE_TELEMETRY_REDIS_URL` and +# remove the sidecar services. See docker/docs/configuring-telemetry.md. services: fiftyone-app: extends: @@ -25,3 +30,64 @@ services: extends: file: ../common-services.yaml service: teams-cas-common + + telemetry-redis: + image: ${TELEMETRY_REDIS_IMAGE:-redis:7-alpine} + command: + - redis-server + - --save + - "60" + - "1" + - --dir + - /data + - --maxmemory + - ${TELEMETRY_REDIS_MAXMEMORY:-400mb} + - --maxmemory-policy + - allkeys-lru + restart: always + volumes: + - telemetry-redis-data:/data + + # One sidecar per observed service. `pid: "service:"` joins the + # target's PID namespace so the sidecar can read /proc//fd/1 and + # psutil can see the target's process. + fiftyone-app-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:fiftyone-app" + cap_add: + - SYS_PTRACE + environment: + POD_NAME: fiftyone-app + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: fiftyone-app + TARGET_NAME: ${FIFTYONE_APP_TARGET_NAME:-hypercorn} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + depends_on: + fiftyone-app: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + + teams-api-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:teams-api" + cap_add: + - SYS_PTRACE + environment: + POD_NAME: teams-api + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: teams-api + TARGET_NAME: ${TEAMS_API_TARGET_NAME:-fiftyone-teams-api} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + depends_on: + teams-api: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + +volumes: + telemetry-redis-data: diff --git a/docker/legacy-auth/compose.dedicated-plugins.yaml b/docker/legacy-auth/compose.dedicated-plugins.yaml index ace9a32c2..aa58adbf1 100644 --- a/docker/legacy-auth/compose.dedicated-plugins.yaml +++ b/docker/legacy-auth/compose.dedicated-plugins.yaml @@ -38,5 +38,80 @@ services: volumes: - plugins-vol:/opt/plugins:ro + telemetry-redis: + image: ${TELEMETRY_REDIS_IMAGE:-redis:7-alpine} + command: + - redis-server + - --save + - "60" + - "1" + - --dir + - /data + - --maxmemory + - ${TELEMETRY_REDIS_MAXMEMORY:-400mb} + - --maxmemory-policy + - allkeys-lru + restart: always + volumes: + - telemetry-redis-data:/data + + fiftyone-app-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:fiftyone-app" + cap_add: + - SYS_PTRACE + environment: + POD_NAME: fiftyone-app + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: fiftyone-app + TARGET_NAME: ${FIFTYONE_APP_TARGET_NAME:-hypercorn} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + depends_on: + fiftyone-app: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + + teams-api-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:teams-api" + cap_add: + - SYS_PTRACE + environment: + POD_NAME: teams-api + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: teams-api + TARGET_NAME: ${TEAMS_API_TARGET_NAME:-fiftyone-teams-api} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + depends_on: + teams-api: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + + teams-plugins-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:teams-plugins" + cap_add: + - SYS_PTRACE + environment: + POD_NAME: teams-plugins + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: teams-plugins + TARGET_NAME: ${TEAMS_PLUGINS_TARGET_NAME:-hypercorn} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + depends_on: + teams-plugins: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + volumes: plugins-vol: + telemetry-redis-data: diff --git a/docker/legacy-auth/compose.delegated-operators.gpu.yaml b/docker/legacy-auth/compose.delegated-operators.gpu.yaml index 6f38f07bb..a6de978d8 100644 --- a/docker/legacy-auth/compose.delegated-operators.gpu.yaml +++ b/docker/legacy-auth/compose.delegated-operators.gpu.yaml @@ -10,10 +10,11 @@ # -f compose.yaml \ # -f compose.delegated-operators.yaml \ # -f compose.delegated-operators.gpu.yaml \ -# -f compose.telemetry.yaml \ -# -f compose.telemetry.delegated-operators.yaml \ # up -d # +# Telemetry (redis + paired sidecars) is bundled by default in the base +# compose.yaml and compose.delegated-operators.yaml files. +# # For Proxy Server instructions please see # https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies services: diff --git a/docker/legacy-auth/compose.delegated-operators.yaml b/docker/legacy-auth/compose.delegated-operators.yaml index 5337711f8..2daddc633 100644 --- a/docker/legacy-auth/compose.delegated-operators.yaml +++ b/docker/legacy-auth/compose.delegated-operators.yaml @@ -1,4 +1,12 @@ --- +# Layered on top of a base compose file (e.g. compose.yaml). The base +# compose file is expected to provide the `telemetry-redis` service. +# +# `teams-do` is forced to `replicas: 1` (see teams-do-common in +# ../common-services.yaml) so the paired `teams-do-telemetry` sidecar's +# `pid: "service:teams-do"` joins a single deterministic PID namespace. +# For multi-worker patterns see docker/docs/configuring-telemetry.md. +# # For Proxy Server instructions please see # https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies services: @@ -7,5 +15,31 @@ services: file: ../common-services.yaml service: teams-do-common + teams-do-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:teams-do" + cap_add: + - SYS_PTRACE + environment: + POD_NAME: teams-do + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: delegated-operator + TARGET_NAME: ${TEAMS_DO_TARGET_NAME:-fiftyone delegated} + EXECUTOR_SIDECAR: "true" + TELEMETRY_SOCKET: /tmp/telemetry/agent.sock + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + FIFTYONE_DATABASE_URI: ${FIFTYONE_DATABASE_URI} + FIFTYONE_DATABASE_NAME: ${FIFTYONE_DATABASE_NAME} + volumes: + - telemetry-socket:/tmp/telemetry + depends_on: + teams-do: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + volumes: plugins-vol: + telemetry-socket: diff --git a/docker/legacy-auth/compose.plugins.yaml b/docker/legacy-auth/compose.plugins.yaml index c71f27224..f2bea08cb 100644 --- a/docker/legacy-auth/compose.plugins.yaml +++ b/docker/legacy-auth/compose.plugins.yaml @@ -33,5 +33,61 @@ services: CAS_URL: ${AUTH0_BASE_URL} NEXTAUTH_URL: ${AUTH0_BASE_URL}/cas/api/auth + telemetry-redis: + image: ${TELEMETRY_REDIS_IMAGE:-redis:7-alpine} + command: + - redis-server + - --save + - "60" + - "1" + - --dir + - /data + - --maxmemory + - ${TELEMETRY_REDIS_MAXMEMORY:-400mb} + - --maxmemory-policy + - allkeys-lru + restart: always + volumes: + - telemetry-redis-data:/data + + fiftyone-app-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:fiftyone-app" + cap_add: + - SYS_PTRACE + environment: + POD_NAME: fiftyone-app + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: fiftyone-app + TARGET_NAME: ${FIFTYONE_APP_TARGET_NAME:-hypercorn} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + depends_on: + fiftyone-app: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + + teams-api-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:teams-api" + cap_add: + - SYS_PTRACE + environment: + POD_NAME: teams-api + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: teams-api + TARGET_NAME: ${TEAMS_API_TARGET_NAME:-fiftyone-teams-api} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + depends_on: + teams-api: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + volumes: plugins-vol: + telemetry-redis-data: diff --git a/docker/legacy-auth/compose.telemetry.delegated-operators.yaml b/docker/legacy-auth/compose.telemetry.delegated-operators.yaml deleted file mode 100644 index f6a1c7d51..000000000 --- a/docker/legacy-auth/compose.telemetry.delegated-operators.yaml +++ /dev/null @@ -1,62 +0,0 @@ ---- -# Telemetry overlay for the delegated operators (teams-do) service. -# Use alongside compose.telemetry.yaml and compose.delegated-operators.yaml: -# -# docker compose -f compose.yaml \ -# -f compose.delegated-operators.yaml \ -# -f compose.telemetry.yaml \ -# -f compose.telemetry.delegated-operators.yaml \ -# up -d -# -# The EXECUTOR_SIDECAR mode watches the teams-do executor for per-operation -# child processes and records per-op metrics back to the delegated_ops -# MongoDB document (via FIFTYONE_DATABASE_URI). -# -# This overlay FORCES `teams-do` replicas to 1 so the sidecar has a -# deterministic 1:1 pairing via the shared PID namespace. docker-compose's -# `pid: "service:"` only joins a single replica's PID namespace, -# so running multiple replicas with a single sidecar would silently miss -# the other replicas. If you need multiple DO workers observed, run one -# teams-do + sidecar pair per worker by defining additional services -# (teams-do-1, teams-do-2, ...), or switch to the helm chart which scales -# the sidecar alongside each pod automatically. -# -# For Proxy Server instructions please see -# https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies -services: - teams-do: - environment: - FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} - TELEMETRY_SOCKET: /tmp/telemetry/agent.sock - deploy: - replicas: 1 - volumes: - - telemetry-socket:/tmp/telemetry - - teams-do-telemetry: - image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} - pid: "service:teams-do" - cap_add: - - SYS_PTRACE - environment: - POD_NAME: teams-do - POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} - SERVICE_TYPE: delegated-operator - TARGET_NAME: ${TEAMS_DO_TARGET_NAME:-fiftyone delegated} - EXECUTOR_SIDECAR: "true" - TELEMETRY_SOCKET: /tmp/telemetry/agent.sock - FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} - FIFTYONE_DATABASE_URI: ${FIFTYONE_DATABASE_URI} - FIFTYONE_DATABASE_NAME: ${FIFTYONE_DATABASE_NAME} - volumes: - - telemetry-socket:/tmp/telemetry - depends_on: - teams-do: - condition: service_started - restart: true - telemetry-redis: - condition: service_started - restart: always - -volumes: - telemetry-socket: diff --git a/docker/legacy-auth/compose.telemetry.plugins.yaml b/docker/legacy-auth/compose.telemetry.plugins.yaml deleted file mode 100644 index ac1de8bf2..000000000 --- a/docker/legacy-auth/compose.telemetry.plugins.yaml +++ /dev/null @@ -1,37 +0,0 @@ ---- -# Telemetry overlay for the dedicated teams-plugins service. The sidecar -# shares the target's PID namespace via `pid: "service:teams-plugins"`. -# -# Layered after compose.dedicated-plugins.yaml + compose.telemetry.yaml: -# -# docker compose -f compose.yaml \ -# -f compose.dedicated-plugins.yaml \ -# -f compose.telemetry.yaml \ -# -f compose.telemetry.plugins.yaml \ -# up -d -# -# For Proxy Server instructions please see -# https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies -services: - teams-plugins: - environment: - FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} - - teams-plugins-telemetry: - image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} - pid: "service:teams-plugins" - cap_add: - - SYS_PTRACE - environment: - POD_NAME: teams-plugins - POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} - SERVICE_TYPE: teams-plugins - TARGET_NAME: ${TEAMS_PLUGINS_TARGET_NAME:-hypercorn} - FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} - depends_on: - teams-plugins: - condition: service_started - restart: true - telemetry-redis: - condition: service_started - restart: always diff --git a/docker/legacy-auth/compose.telemetry.yaml b/docker/legacy-auth/compose.telemetry.yaml deleted file mode 100644 index 89b9d57a4..000000000 --- a/docker/legacy-auth/compose.telemetry.yaml +++ /dev/null @@ -1,90 +0,0 @@ ---- -# Telemetry overlay. Adds a Redis backend + sidecar containers that observe -# each target service via a shared PID namespace, publishing metrics and -# log events to Redis. -# -# Layered on top of the main compose file, e.g.: -# docker compose -f compose.yaml -f compose.telemetry.yaml up -# -# Or with delegated operators and/or dedicated plugins: -# docker compose -f compose.yaml \ -# -f compose.delegated-operators.yaml \ -# -f compose.dedicated-plugins.yaml \ -# -f compose.telemetry.yaml up -# -# For Proxy Server instructions please see -# https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies -services: - telemetry-redis: - image: ${TELEMETRY_REDIS_IMAGE:-redis:7-alpine} - command: - - redis-server - - --save - - "60" - - "1" - - --dir - - /data - - --maxmemory - - ${TELEMETRY_REDIS_MAXMEMORY:-400mb} - - --maxmemory-policy - - allkeys-lru - restart: always - volumes: - - telemetry-redis-data:/data - - fiftyone-app: - environment: - FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} - - teams-api: - environment: - FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} - - teams-app: - environment: - FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} - - # One sidecar per observed service. `pid: "service:"` joins the - # target's PID namespace so the sidecar can read /proc//fd/1 and - # psutil can see the target's process. - - fiftyone-app-telemetry: - image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} - pid: "service:fiftyone-app" - cap_add: - - SYS_PTRACE - environment: - POD_NAME: fiftyone-app - POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} - SERVICE_TYPE: fiftyone-app - TARGET_NAME: ${FIFTYONE_APP_TARGET_NAME:-hypercorn} - FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} - depends_on: - fiftyone-app: - condition: service_started - restart: true - telemetry-redis: - condition: service_started - restart: always - - teams-api-telemetry: - image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} - pid: "service:teams-api" - cap_add: - - SYS_PTRACE - environment: - POD_NAME: teams-api - POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} - SERVICE_TYPE: teams-api - TARGET_NAME: ${TEAMS_API_TARGET_NAME:-fiftyone-teams-api} - FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} - depends_on: - teams-api: - condition: service_started - restart: true - telemetry-redis: - condition: service_started - restart: always - -volumes: - telemetry-redis-data: diff --git a/docker/legacy-auth/compose.yaml b/docker/legacy-auth/compose.yaml index c463cf25e..447c1ddf3 100644 --- a/docker/legacy-auth/compose.yaml +++ b/docker/legacy-auth/compose.yaml @@ -1,6 +1,11 @@ --- # For Proxy Server instructions please see # https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies +# +# Telemetry (`telemetry-redis` + per-service sidecars) is enabled by +# default. To opt out, scale the telemetry services to 0 in a +# `compose.override.yaml`, or unset `FIFTYONE_TELEMETRY_REDIS_URL` and +# remove the sidecar services. See docker/docs/configuring-telemetry.md. services: fiftyone-app: extends: @@ -24,3 +29,64 @@ services: extends: file: ../common-services.yaml service: teams-cas-common + + telemetry-redis: + image: ${TELEMETRY_REDIS_IMAGE:-redis:7-alpine} + command: + - redis-server + - --save + - "60" + - "1" + - --dir + - /data + - --maxmemory + - ${TELEMETRY_REDIS_MAXMEMORY:-400mb} + - --maxmemory-policy + - allkeys-lru + restart: always + volumes: + - telemetry-redis-data:/data + + # One sidecar per observed service. `pid: "service:"` joins the + # target's PID namespace so the sidecar can read /proc//fd/1 and + # psutil can see the target's process. + fiftyone-app-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:fiftyone-app" + cap_add: + - SYS_PTRACE + environment: + POD_NAME: fiftyone-app + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: fiftyone-app + TARGET_NAME: ${FIFTYONE_APP_TARGET_NAME:-hypercorn} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + depends_on: + fiftyone-app: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + + teams-api-telemetry: + image: ${TELEMETRY_SIDECAR_IMAGE:-voxel51/telemetry-sidecar:latest} + pid: "service:teams-api" + cap_add: + - SYS_PTRACE + environment: + POD_NAME: teams-api + POD_NAMESPACE: ${TELEMETRY_NAMESPACE:-docker} + SERVICE_TYPE: teams-api + TARGET_NAME: ${TEAMS_API_TARGET_NAME:-fiftyone-teams-api} + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + depends_on: + teams-api: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + +volumes: + telemetry-redis-data: diff --git a/docs/orchestrators/configuring-kubernetes-orchestrator.md b/docs/orchestrators/configuring-kubernetes-orchestrator.md index a6f8d6bd8..8e614d5bb 100644 --- a/docs/orchestrators/configuring-kubernetes-orchestrator.md +++ b/docs/orchestrators/configuring-kubernetes-orchestrator.md @@ -462,12 +462,12 @@ fom.register_orchestrator( ## Optional: Telemetry Sidecar -If your deployment runs the telemetry overlay (see the helm chart's -`extraContainers` / `nativeSidecarContainers` options or the docker -`compose.telemetry.yaml`), you can attach a per-Job telemetry sidecar to -on-demand Kubernetes orchestrators as well. This emits per-operation -metrics back to the same Redis backend so the Settings → Metrics page -sees individual delegated runs. +If your deployment runs telemetry (see the helm chart's +`extraContainers` / `nativeSidecarContainers` options, or the docker +compose files where it is bundled by default), you can attach a per-Job +telemetry sidecar to on-demand Kubernetes orchestrators as well. This +emits per-operation metrics back to the same Redis backend so the +Settings → Metrics page sees individual delegated runs. Use a Kubernetes [native sidecar](https://kubernetes.io/docs/concepts/workloads/pods/sidecar-containers/) diff --git a/tests/unit/compose/docker-compose-internal-auth_test.go b/tests/unit/compose/docker-compose-internal-auth_test.go index cc9f911bc..84f19d2cc 100644 --- a/tests/unit/compose/docker-compose-internal-auth_test.go +++ b/tests/unit/compose/docker-compose-internal-auth_test.go @@ -29,9 +29,6 @@ var internalAuthComposePluginsFile = filepath.Join(dockerInternalAuthDir, "compo var internalAuthComposeDedicatedPluginsFile = filepath.Join(dockerInternalAuthDir, "compose.dedicated-plugins.yaml") var internalAuthComposeDelegatedOperationsFile = filepath.Join(dockerInternalAuthDir, "compose.delegated-operators.yaml") var internalAuthComposeDelegatedOperationsGpuFile = filepath.Join(dockerInternalAuthDir, "compose.delegated-operators.gpu.yaml") -var internalAuthComposeTelemetryFile = filepath.Join(dockerInternalAuthDir, "compose.telemetry.yaml") -var internalAuthComposeTelemetryPluginsFile = filepath.Join(dockerInternalAuthDir, "compose.telemetry.plugins.yaml") -var internalAuthComposeTelemetryDelegatedOperatorsFile = filepath.Join(dockerInternalAuthDir, "compose.telemetry.delegated-operators.yaml") var internalAuthEnvTemplateFilePath = filepath.Join(dockerInternalAuthDir, "env.template") type commonServicesInternalAuthDockerComposeTest struct { @@ -73,9 +70,12 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServicesNames() { nil, []string{ "fiftyone-app", + "fiftyone-app-telemetry", "teams-api", + "teams-api-telemetry", "teams-app", "teams-cas", + "telemetry-redis", }, }, { @@ -83,45 +83,6 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServicesNames() { []string{internalAuthComposePluginsFile}, s.dotEnvFiles, nil, - []string{ - "fiftyone-app", - "teams-api", - "teams-app", - "teams-cas", - }, - }, - { - "composeDedicatedPlugins", - []string{internalAuthComposeDedicatedPluginsFile}, - s.dotEnvFiles, - nil, - []string{ - "fiftyone-app", - "teams-api", - "teams-app", - "teams-cas", - "teams-plugins", - }, - }, - { - "composeDelegatedOperations", - []string{internalAuthComposeDelegatedOperationsFile}, - s.dotEnvFiles, - nil, - []string{ - "teams-do", - }, - }, - { - "composeDelegatedOperationsGpu", - []string{ - internalAuthComposeFile, - internalAuthComposeDelegatedOperationsFile, - internalAuthComposeDelegatedOperationsGpuFile, - internalAuthComposeTelemetryFile, - }, - s.dotEnvFiles, - []string{"gpu"}, []string{ "fiftyone-app", "fiftyone-app-telemetry", @@ -129,15 +90,12 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServicesNames() { "teams-api-telemetry", "teams-app", "teams-cas", - "teams-do", - "teams-do-gpu", - "teams-do-gpu-telemetry", "telemetry-redis", }, }, { - "composeTelemetry", - []string{internalAuthComposeFile, internalAuthComposeTelemetryFile}, + "composeDedicatedPlugins", + []string{internalAuthComposeDedicatedPluginsFile}, s.dotEnvFiles, nil, []string{ @@ -147,17 +105,14 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServicesNames() { "teams-api-telemetry", "teams-app", "teams-cas", + "teams-plugins", + "teams-plugins-telemetry", "telemetry-redis", }, }, { - "composeTelemetryPlugins", - []string{ - internalAuthComposeFile, - internalAuthComposeDedicatedPluginsFile, - internalAuthComposeTelemetryFile, - internalAuthComposeTelemetryPluginsFile, - }, + "composeDelegatedOperations", + []string{internalAuthComposeFile, internalAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, nil, []string{ @@ -167,21 +122,20 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServicesNames() { "teams-api-telemetry", "teams-app", "teams-cas", - "teams-plugins", - "teams-plugins-telemetry", + "teams-do", + "teams-do-telemetry", "telemetry-redis", }, }, { - "composeTelemetryDelegatedOperators", + "composeDelegatedOperationsGpu", []string{ internalAuthComposeFile, internalAuthComposeDelegatedOperationsFile, - internalAuthComposeTelemetryFile, - internalAuthComposeTelemetryDelegatedOperatorsFile, + internalAuthComposeDelegatedOperationsGpuFile, }, s.dotEnvFiles, - nil, + []string{"gpu"}, []string{ "fiftyone-app", "fiftyone-app-telemetry", @@ -190,6 +144,8 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServicesNames() { "teams-app", "teams-cas", "teams-do", + "teams-do-gpu", + "teams-do-gpu-telemetry", "teams-do-telemetry", "telemetry-redis", }, @@ -275,28 +231,28 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceImage() { { "delegatedOperationsTeamsDo", "teams-do", - []string{internalAuthComposeDelegatedOperationsFile}, + []string{internalAuthComposeFile, internalAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, "voxel51/fiftyone-teams-cv-full:v2.18.1", }, { "telemetryRedis", "telemetry-redis", - []string{internalAuthComposeFile, internalAuthComposeTelemetryFile}, + []string{internalAuthComposeFile}, s.dotEnvFiles, "redis:7-alpine", }, { "telemetrySidecarFiftyoneApp", "fiftyone-app-telemetry", - []string{internalAuthComposeFile, internalAuthComposeTelemetryFile}, + []string{internalAuthComposeFile}, s.dotEnvFiles, "voxel51/telemetry-sidecar:latest", }, { "telemetrySidecarTeamsApi", "teams-api-telemetry", - []string{internalAuthComposeFile, internalAuthComposeTelemetryFile}, + []string{internalAuthComposeFile}, s.dotEnvFiles, "voxel51/telemetry-sidecar:latest", }, @@ -306,12 +262,17 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceImage() { []string{ internalAuthComposeFile, internalAuthComposeDelegatedOperationsFile, - internalAuthComposeTelemetryFile, - internalAuthComposeTelemetryDelegatedOperatorsFile, }, s.dotEnvFiles, "voxel51/telemetry-sidecar:latest", }, + { + "telemetrySidecarTeamsPlugins", + "teams-plugins-telemetry", + []string{internalAuthComposeDedicatedPluginsFile}, + s.dotEnvFiles, + "voxel51/telemetry-sidecar:latest", + }, } for _, testCase := range testCases { @@ -375,6 +336,7 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_MEDIA_CACHE_APP_IMAGES=false", "FIFTYONE_MEDIA_CACHE_SIZE_BYTES=-1", "FIFTYONE_SIGNED_URL_EXPIRATION=24", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", }, }, { @@ -392,6 +354,7 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_ENV=production", "FIFTYONE_INTERNAL_SERVICE=true", "FIFTYONE_LOGGING_FORMAT=text", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", "GRAPHQL_DEFAULT_LIMIT=10", "LOGGING_LEVEL=INFO", "MONGO_DEFAULT_DB=fiftyone", @@ -412,6 +375,7 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_SERVER_ADDRESS=", "FIFTYONE_SERVER_PATH_PREFIX=/api/proxy/fiftyone-teams", "FIFTYONE_TEAMS_PROXY_URL=http://fiftyone-app:5151", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", "NODE_ENV=production", "RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED=false", "FIFTYONE_APP_ANONYMOUS_ANALYTICS_ENABLED=true", @@ -459,6 +423,7 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_MEDIA_CACHE_SIZE_BYTES=-1", "FIFTYONE_PLUGINS_DIR=/opt/plugins", "FIFTYONE_SIGNED_URL_EXPIRATION=24", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", }, }, { @@ -476,6 +441,7 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_ENV=production", "FIFTYONE_INTERNAL_SERVICE=true", "FIFTYONE_LOGGING_FORMAT=text", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", "GRAPHQL_DEFAULT_LIMIT=10", "LOGGING_LEVEL=INFO", "MONGO_DEFAULT_DB=fiftyone", @@ -497,6 +463,7 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_SERVER_ADDRESS=", "FIFTYONE_SERVER_PATH_PREFIX=/api/proxy/fiftyone-teams", "FIFTYONE_TEAMS_PROXY_URL=http://fiftyone-app:5151", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", "NODE_ENV=production", "RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED=false", "FIFTYONE_APP_ANONYMOUS_ANALYTICS_ENABLED=true", @@ -543,6 +510,7 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_MEDIA_CACHE_APP_IMAGES=false", "FIFTYONE_MEDIA_CACHE_SIZE_BYTES=-1", "FIFTYONE_SIGNED_URL_EXPIRATION=24", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", }, }, { @@ -560,6 +528,7 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_ENV=production", "FIFTYONE_INTERNAL_SERVICE=true", "FIFTYONE_LOGGING_FORMAT=text", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", "GRAPHQL_DEFAULT_LIMIT=10", "LOGGING_LEVEL=INFO", "MONGO_DEFAULT_DB=fiftyone", @@ -581,6 +550,7 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_SERVER_ADDRESS=", "FIFTYONE_SERVER_PATH_PREFIX=/api/proxy/fiftyone-teams", "FIFTYONE_TEAMS_PROXY_URL=http://fiftyone-app:5151", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", "NODE_ENV=production", "RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED=false", "FIFTYONE_TEAMS_PLUGIN_URL=http://teams-plugins:5151", @@ -626,12 +596,13 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_MEDIA_CACHE_APP_IMAGES=false", "FIFTYONE_MEDIA_CACHE_SIZE_BYTES=-1", "FIFTYONE_PLUGINS_DIR=/opt/plugins", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", }, }, { "delegatedOperationsTeamsDo", "teams-do", - []string{internalAuthComposeDelegatedOperationsFile}, + []string{internalAuthComposeFile, internalAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, []string{ "API_URL=http://teams-api:8000", @@ -642,6 +613,8 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_INTERNAL_SERVICE=true", "FIFTYONE_MEDIA_CACHE_SIZE_BYTES=-1", "FIFTYONE_PLUGINS_DIR=/opt/plugins", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", + "TELEMETRY_SOCKET=/tmp/telemetry/agent.sock", }, }, } @@ -832,7 +805,7 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceRestart() { { "delegatedOperationsTeamsDo", "teams-do", - []string{internalAuthComposeDelegatedOperationsFile}, + []string{internalAuthComposeFile, internalAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, types.RestartPolicyAlways, }, @@ -1030,7 +1003,7 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceVolumes() { { "delegatedOperationsTeamsDo", "teams-do", - []string{internalAuthComposeDelegatedOperationsFile}, + []string{internalAuthComposeFile, internalAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, []types.ServiceVolumeConfig{ { @@ -1040,6 +1013,13 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceVolumes() { ReadOnly: true, Volume: &types.ServiceVolumeVolume{}, }, + { + Type: "volume", + Source: "telemetry-socket", + Target: "/tmp/telemetry", + ReadOnly: false, + Volume: &types.ServiceVolumeVolume{}, + }, }, }, } @@ -1087,7 +1067,11 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestVolumes() { "default", []string{internalAuthComposeFile}, s.dotEnvFiles, - nil, + types.Volumes{ + "telemetry-redis-data": { + Name: "fiftyone-compose-test_telemetry-redis-data", + }, + }, }, { "plugins", @@ -1097,6 +1081,9 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestVolumes() { "plugins-vol": { Name: "fiftyone-compose-test_plugins-vol", }, + "telemetry-redis-data": { + Name: "fiftyone-compose-test_telemetry-redis-data", + }, }, }, { @@ -1107,16 +1094,25 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestVolumes() { "plugins-vol": { Name: "fiftyone-compose-test_plugins-vol", }, + "telemetry-redis-data": { + Name: "fiftyone-compose-test_telemetry-redis-data", + }, }, }, { "delegatedOperations", - []string{internalAuthComposeDelegatedOperationsFile}, + []string{internalAuthComposeFile, internalAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, types.Volumes{ "plugins-vol": { Name: "fiftyone-compose-test_plugins-vol", }, + "telemetry-redis-data": { + Name: "fiftyone-compose-test_telemetry-redis-data", + }, + "telemetry-socket": { + Name: "fiftyone-compose-test_telemetry-socket", + }, }, }, } diff --git a/tests/unit/compose/docker-compose-legacy-auth_test.go b/tests/unit/compose/docker-compose-legacy-auth_test.go index c44c2fb86..fcbb3d72c 100644 --- a/tests/unit/compose/docker-compose-legacy-auth_test.go +++ b/tests/unit/compose/docker-compose-legacy-auth_test.go @@ -29,9 +29,6 @@ var legacyAuthComposePluginsFile = filepath.Join(dockerLegacyAuthDir, "compose.p var legacyAuthComposeDedicatedPluginsFile = filepath.Join(dockerLegacyAuthDir, "compose.dedicated-plugins.yaml") var legacyAuthComposeDelegatedOperationsFile = filepath.Join(dockerLegacyAuthDir, "compose.delegated-operators.yaml") var legacyAuthComposeDelegatedOperationsGpuFile = filepath.Join(dockerLegacyAuthDir, "compose.delegated-operators.gpu.yaml") -var legacyAuthComposeTelemetryFile = filepath.Join(dockerLegacyAuthDir, "compose.telemetry.yaml") -var legacyAuthComposeTelemetryPluginsFile = filepath.Join(dockerLegacyAuthDir, "compose.telemetry.plugins.yaml") -var legacyAuthComposeTelemetryDelegatedOperatorsFile = filepath.Join(dockerLegacyAuthDir, "compose.telemetry.delegated-operators.yaml") var legacyAuthEnvTemplateFilePath = filepath.Join(dockerLegacyAuthDir, "env.template") type commonServicesLegacyAuthDockerComposeTest struct { @@ -73,9 +70,12 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServicesNames() { nil, []string{ "fiftyone-app", + "fiftyone-app-telemetry", "teams-api", + "teams-api-telemetry", "teams-app", "teams-cas", + "telemetry-redis", }, }, { @@ -83,45 +83,6 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServicesNames() { []string{legacyAuthComposePluginsFile}, s.dotEnvFiles, nil, - []string{ - "fiftyone-app", - "teams-api", - "teams-app", - "teams-cas", - }, - }, - { - "composeDedicatedPlugins", - []string{legacyAuthComposeDedicatedPluginsFile}, - s.dotEnvFiles, - nil, - []string{ - "fiftyone-app", - "teams-api", - "teams-app", - "teams-cas", - "teams-plugins", - }, - }, - { - "composeDelegatedOperations", - []string{legacyAuthComposeDelegatedOperationsFile}, - s.dotEnvFiles, - nil, - []string{ - "teams-do", - }, - }, - { - "composeDelegatedOperationsGpu", - []string{ - legacyAuthComposeFile, - legacyAuthComposeDelegatedOperationsFile, - legacyAuthComposeDelegatedOperationsGpuFile, - legacyAuthComposeTelemetryFile, - }, - s.dotEnvFiles, - []string{"gpu"}, []string{ "fiftyone-app", "fiftyone-app-telemetry", @@ -129,15 +90,12 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServicesNames() { "teams-api-telemetry", "teams-app", "teams-cas", - "teams-do", - "teams-do-gpu", - "teams-do-gpu-telemetry", "telemetry-redis", }, }, { - "composeTelemetry", - []string{legacyAuthComposeFile, legacyAuthComposeTelemetryFile}, + "composeDedicatedPlugins", + []string{legacyAuthComposeDedicatedPluginsFile}, s.dotEnvFiles, nil, []string{ @@ -147,17 +105,14 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServicesNames() { "teams-api-telemetry", "teams-app", "teams-cas", + "teams-plugins", + "teams-plugins-telemetry", "telemetry-redis", }, }, { - "composeTelemetryPlugins", - []string{ - legacyAuthComposeFile, - legacyAuthComposeDedicatedPluginsFile, - legacyAuthComposeTelemetryFile, - legacyAuthComposeTelemetryPluginsFile, - }, + "composeDelegatedOperations", + []string{legacyAuthComposeFile, legacyAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, nil, []string{ @@ -167,21 +122,20 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServicesNames() { "teams-api-telemetry", "teams-app", "teams-cas", - "teams-plugins", - "teams-plugins-telemetry", + "teams-do", + "teams-do-telemetry", "telemetry-redis", }, }, { - "composeTelemetryDelegatedOperators", + "composeDelegatedOperationsGpu", []string{ legacyAuthComposeFile, legacyAuthComposeDelegatedOperationsFile, - legacyAuthComposeTelemetryFile, - legacyAuthComposeTelemetryDelegatedOperatorsFile, + legacyAuthComposeDelegatedOperationsGpuFile, }, s.dotEnvFiles, - nil, + []string{"gpu"}, []string{ "fiftyone-app", "fiftyone-app-telemetry", @@ -190,6 +144,8 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServicesNames() { "teams-app", "teams-cas", "teams-do", + "teams-do-gpu", + "teams-do-gpu-telemetry", "teams-do-telemetry", "telemetry-redis", }, @@ -275,28 +231,28 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceImage() { { "delegatedOperationsTeamsDo", "teams-do", - []string{legacyAuthComposeDelegatedOperationsFile}, + []string{legacyAuthComposeFile, legacyAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, "voxel51/fiftyone-teams-cv-full:v2.18.1", }, { "telemetryRedis", "telemetry-redis", - []string{legacyAuthComposeFile, legacyAuthComposeTelemetryFile}, + []string{legacyAuthComposeFile}, s.dotEnvFiles, "redis:7-alpine", }, { "telemetrySidecarFiftyoneApp", "fiftyone-app-telemetry", - []string{legacyAuthComposeFile, legacyAuthComposeTelemetryFile}, + []string{legacyAuthComposeFile}, s.dotEnvFiles, "voxel51/telemetry-sidecar:latest", }, { "telemetrySidecarTeamsApi", "teams-api-telemetry", - []string{legacyAuthComposeFile, legacyAuthComposeTelemetryFile}, + []string{legacyAuthComposeFile}, s.dotEnvFiles, "voxel51/telemetry-sidecar:latest", }, @@ -306,12 +262,17 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceImage() { []string{ legacyAuthComposeFile, legacyAuthComposeDelegatedOperationsFile, - legacyAuthComposeTelemetryFile, - legacyAuthComposeTelemetryDelegatedOperatorsFile, }, s.dotEnvFiles, "voxel51/telemetry-sidecar:latest", }, + { + "telemetrySidecarTeamsPlugins", + "teams-plugins-telemetry", + []string{legacyAuthComposeDedicatedPluginsFile}, + s.dotEnvFiles, + "voxel51/telemetry-sidecar:latest", + }, } for _, testCase := range testCases { @@ -375,6 +336,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_MEDIA_CACHE_APP_IMAGES=false", "FIFTYONE_MEDIA_CACHE_SIZE_BYTES=-1", "FIFTYONE_SIGNED_URL_EXPIRATION=24", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", }, }, { @@ -392,6 +354,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_ENV=production", "FIFTYONE_INTERNAL_SERVICE=true", "FIFTYONE_LOGGING_FORMAT=text", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", "GRAPHQL_DEFAULT_LIMIT=10", "LOGGING_LEVEL=INFO", "MONGO_DEFAULT_DB=fiftyone", @@ -412,6 +375,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_SERVER_ADDRESS=", "FIFTYONE_SERVER_PATH_PREFIX=/api/proxy/fiftyone-teams", "FIFTYONE_TEAMS_PROXY_URL=http://fiftyone-app:5151", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", "NODE_ENV=production", "RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED=false", "FIFTYONE_APP_ANONYMOUS_ANALYTICS_ENABLED=true", @@ -457,6 +421,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_MEDIA_CACHE_APP_IMAGES=false", "FIFTYONE_MEDIA_CACHE_SIZE_BYTES=-1", "FIFTYONE_SIGNED_URL_EXPIRATION=24", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", }, }, { @@ -474,6 +439,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_ENV=production", "FIFTYONE_INTERNAL_SERVICE=true", "FIFTYONE_LOGGING_FORMAT=text", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", "GRAPHQL_DEFAULT_LIMIT=10", "LOGGING_LEVEL=INFO", "MONGO_DEFAULT_DB=fiftyone", @@ -494,6 +460,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_SERVER_ADDRESS=", "FIFTYONE_SERVER_PATH_PREFIX=/api/proxy/fiftyone-teams", "FIFTYONE_TEAMS_PROXY_URL=http://fiftyone-app:5151", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", "NODE_ENV=production", "RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED=false", "FIFTYONE_APP_ANONYMOUS_ANALYTICS_ENABLED=true", @@ -540,6 +507,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_MEDIA_CACHE_SIZE_BYTES=-1", "FIFTYONE_PLUGINS_DIR=/opt/plugins", "FIFTYONE_SIGNED_URL_EXPIRATION=24", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", }, }, { @@ -558,6 +526,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_INTERNAL_SERVICE=true", "FIFTYONE_LOGGING_FORMAT=text", "FIFTYONE_PLUGINS_DIR=/opt/plugins", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", "GRAPHQL_DEFAULT_LIMIT=10", "LOGGING_LEVEL=INFO", "MONGO_DEFAULT_DB=fiftyone", @@ -578,6 +547,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_SERVER_ADDRESS=", "FIFTYONE_SERVER_PATH_PREFIX=/api/proxy/fiftyone-teams", "FIFTYONE_TEAMS_PROXY_URL=http://fiftyone-app:5151", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", "NODE_ENV=production", "RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED=false", "FIFTYONE_APP_ANONYMOUS_ANALYTICS_ENABLED=true", @@ -623,6 +593,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_MEDIA_CACHE_APP_IMAGES=false", "FIFTYONE_MEDIA_CACHE_SIZE_BYTES=-1", "FIFTYONE_SIGNED_URL_EXPIRATION=24", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", }, }, { @@ -641,6 +612,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_INTERNAL_SERVICE=true", "FIFTYONE_LOGGING_FORMAT=text", "FIFTYONE_PLUGINS_DIR=/opt/plugins", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", "GRAPHQL_DEFAULT_LIMIT=10", "LOGGING_LEVEL=INFO", "MONGO_DEFAULT_DB=fiftyone", @@ -661,6 +633,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_SERVER_ADDRESS=", "FIFTYONE_SERVER_PATH_PREFIX=/api/proxy/fiftyone-teams", "FIFTYONE_TEAMS_PROXY_URL=http://fiftyone-app:5151", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", "NODE_ENV=production", "RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED=false", "FIFTYONE_TEAMS_PLUGIN_URL=http://teams-plugins:5151", @@ -705,12 +678,13 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_MEDIA_CACHE_APP_IMAGES=false", "FIFTYONE_MEDIA_CACHE_SIZE_BYTES=-1", "FIFTYONE_PLUGINS_DIR=/opt/plugins", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", }, }, { "delegatedOperationsTeamsDo", "teams-do", - []string{legacyAuthComposeDelegatedOperationsFile}, + []string{legacyAuthComposeFile, legacyAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, []string{ "API_URL=http://teams-api:8000", @@ -721,6 +695,8 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceEnvironment() { "FIFTYONE_INTERNAL_SERVICE=true", "FIFTYONE_MEDIA_CACHE_SIZE_BYTES=-1", "FIFTYONE_PLUGINS_DIR=/opt/plugins", + "FIFTYONE_TELEMETRY_REDIS_URL=redis://telemetry-redis:6379", + "TELEMETRY_SOCKET=/tmp/telemetry/agent.sock", }, }, } @@ -916,7 +892,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceRestart() { { "delegatedOperationsTeamsDo", "teams-do", - []string{legacyAuthComposeDelegatedOperationsFile}, + []string{legacyAuthComposeFile, legacyAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, types.RestartPolicyAlways, }, @@ -1114,7 +1090,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceVolumes() { { "delegatedOperationsTeamsDo", "teams-do", - []string{legacyAuthComposeDelegatedOperationsFile}, + []string{legacyAuthComposeFile, legacyAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, []types.ServiceVolumeConfig{ { @@ -1124,6 +1100,13 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceVolumes() { ReadOnly: true, Volume: &types.ServiceVolumeVolume{}, }, + { + Type: "volume", + Source: "telemetry-socket", + Target: "/tmp/telemetry", + ReadOnly: false, + Volume: &types.ServiceVolumeVolume{}, + }, }, }, } @@ -1171,7 +1154,11 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestVolumes() { "default", []string{legacyAuthComposeFile}, s.dotEnvFiles, - nil, + types.Volumes{ + "telemetry-redis-data": { + Name: "fiftyone-compose-test_telemetry-redis-data", + }, + }, }, { "plugins", @@ -1181,6 +1168,9 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestVolumes() { "plugins-vol": { Name: "fiftyone-compose-test_plugins-vol", }, + "telemetry-redis-data": { + Name: "fiftyone-compose-test_telemetry-redis-data", + }, }, }, { @@ -1191,16 +1181,25 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestVolumes() { "plugins-vol": { Name: "fiftyone-compose-test_plugins-vol", }, + "telemetry-redis-data": { + Name: "fiftyone-compose-test_telemetry-redis-data", + }, }, }, { "delegatedOperations", - []string{legacyAuthComposeDelegatedOperationsFile}, + []string{legacyAuthComposeFile, legacyAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, types.Volumes{ "plugins-vol": { Name: "fiftyone-compose-test_plugins-vol", }, + "telemetry-redis-data": { + Name: "fiftyone-compose-test_telemetry-redis-data", + }, + "telemetry-socket": { + Name: "fiftyone-compose-test_telemetry-socket", + }, }, }, } diff --git a/utils/validate-docker-pulls.sh b/utils/validate-docker-pulls.sh index 2af6b79c2..c830e09e5 100755 --- a/utils/validate-docker-pulls.sh +++ b/utils/validate-docker-pulls.sh @@ -16,6 +16,7 @@ set -euo pipefail # exhaustive set. EXPECTED_IMAGES=( "docker.io/busybox:stable-glibc" # For initContainers + "docker.io/redis:7-alpine" # For telemetry redis (telemetry.enabled defaults to true) "voxel51/fiftyone-app" "voxel51/fiftyone-app-gpt" "voxel51/fiftyone-app-torch" From 2ed1278ac2ee8ccf2e2f1748415c0f797665d3aa Mon Sep 17 00:00:00 2001 From: kacey Date: Tue, 19 May 2026 10:55:33 -0700 Subject: [PATCH 17/23] chore: temp fix for tests --- tests/fixtures/docker/compose.override.yaml | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/fixtures/docker/compose.override.yaml b/tests/fixtures/docker/compose.override.yaml index 7b1bcfccc..d1b1f4cdd 100644 --- a/tests/fixtures/docker/compose.override.yaml +++ b/tests/fixtures/docker/compose.override.yaml @@ -2,3 +2,25 @@ services: fiftyone-app: environment: FIFTYONE_DATABASE_ADMIN: false + + # Integration tests don't have access to the private + # `voxel51/telemetry-sidecar` image. Replace each sidecar with a + # public idle alpine container so the stack still composes correctly + # but no private-registry pull is required. Sidecars are not exercised + # by these tests — only the main services are. + fiftyone-app-telemetry: + image: alpine:3.20 + command: ["tail", "-f", "/dev/null"] + pull_policy: missing + teams-api-telemetry: + image: alpine:3.20 + command: ["tail", "-f", "/dev/null"] + pull_policy: missing + teams-plugins-telemetry: + image: alpine:3.20 + command: ["tail", "-f", "/dev/null"] + pull_policy: missing + teams-do-telemetry: + image: alpine:3.20 + command: ["tail", "-f", "/dev/null"] + pull_policy: missing From 9148867827a2ef350b7152356e67c08436e012f4 Mon Sep 17 00:00:00 2001 From: kacey Date: Tue, 19 May 2026 11:57:26 -0700 Subject: [PATCH 18/23] feat: default enable telemetric with all default settings generated --- helm/fiftyone-teams-app/README.md | 7 +- .../templates/_do_targets.tpl | 6 + .../fiftyone-teams-app/templates/_helpers.tpl | 10 ++ .../templates/_telemetry.tpl | 154 ++++++++++++++++++ .../templates/api-deployment.yaml | 5 +- .../templates/app-deployment.yaml | 5 +- ...elegated-operator-instance-deployment.yaml | 30 +++- .../delegated-operator-job-configmap.yaml | 17 +- .../templates/plugins-deployment.yaml | 5 +- .../templates/telemetry-rolebinding.yaml | 6 +- helm/fiftyone-teams-app/values.schema.json | 67 +++++++- helm/fiftyone-teams-app/values.yaml | 41 +++-- tests/unit/helm/api-deployment_test.go | 73 +++++---- tests/unit/helm/app-deployment_test.go | 66 ++++---- ...gated-operator-instance-deployment_test.go | 106 ++++++------ .../delegated-operator-job-configmap_test.go | 14 +- tests/unit/helm/plugins-deployment_test.go | 108 ++++++------ tests/unit/helm/teams-app-deployment_test.go | 66 ++++---- tests/unit/helm/telemetry-rolebinding_test.go | 23 ++- tests/unit/helm/yaml_helpers.go | 14 ++ 20 files changed, 572 insertions(+), 251 deletions(-) diff --git a/helm/fiftyone-teams-app/README.md b/helm/fiftyone-teams-app/README.md index a22049cd4..294a4e786 100644 --- a/helm/fiftyone-teams-app/README.md +++ b/helm/fiftyone-teams-app/README.md @@ -1094,13 +1094,16 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): | teamsAppSettings.updateStrategy | object | `{"type":"RollingUpdate"}` | Control how `teams-app` pods are redeployed during an upgrade. [Reference][upgrade-strategies] | | teamsAppSettings.volumeMounts | list | `[]` | Volume mounts for `teams-app` pods. [Reference][volumes]. | | teamsAppSettings.volumes | list | `[]` | Volumes for `teams-app` pods. [Reference][volumes]. | -| telemetry.enabled | bool | `true` | Whether to render the telemetry Redis Deployment/Service and the telemetry pod-logs Role/RoleBinding. Defaults to `true` so consumers that wire up `telemetry-sidecar` containers get a working redis without additional opt-in. Set to `false` to skip rendering all telemetry-related resources. | +| telemetry.enabled | bool | `true` | Master switch. When `true` (default), all telemetry resources and auto-injected sidecars are rendered. When `false`, none are. | | telemetry.redis.image | string | `"redis:7-alpine"` | Container image for the telemetry Redis Deployment. | | telemetry.redis.maxmemory | string | `"400mb"` | `--maxmemory` flag passed to `redis-server`. | | telemetry.redis.maxmemoryPolicy | string | `"allkeys-lru"` | `--maxmemory-policy` flag passed to `redis-server`. | | telemetry.redis.resources | object | `{"limits":{"cpu":"250m","memory":"512Mi"},"requests":{"cpu":"100m","memory":"256Mi"}}` | Resource requests/limits for the telemetry Redis container. [Reference][resources]. | | telemetry.redis.storage | string | `"1Gi"` | Persistent volume size for the telemetry Redis PVC. | -| telemetry.serviceAccounts | list | `[]` | ServiceAccount names (in `namespace.name`) bound to the telemetry pod-logs Role. Each entry becomes a `ServiceAccount` subject on the generated RoleBinding. When empty, the RoleBinding subjects default to the `default` ServiceAccount. | +| telemetry.serviceAccounts | list | `[]` | ServiceAccount names (in `namespace.name`) bound to the telemetry pod-logs Role. Each entry becomes a `ServiceAccount` subject on the generated RoleBinding. When empty, the RoleBinding subjects default to the chart's main app service account and the teams-api RBAC service account so auto-injected sidecars can read their target's logs. | +| telemetry.sidecar.image | string | `"voxel51/telemetry-sidecar:latest"` | Container image for the auto-injected `telemetry-sidecar` containers. | +| telemetry.sidecar.imagePullPolicy | string | `"Always"` | Pull policy for the sidecar image. | +| telemetry.sidecar.resources | object | `{"limits":{"cpu":"100m","memory":"512Mi"},"requests":{"cpu":"100m","memory":"512Mi"}}` | Resource requests/limits for each auto-injected sidecar container. | diff --git a/helm/fiftyone-teams-app/templates/_do_targets.tpl b/helm/fiftyone-teams-app/templates/_do_targets.tpl index 7806b82ca..3a90d039b 100644 --- a/helm/fiftyone-teams-app/templates/_do_targets.tpl +++ b/helm/fiftyone-teams-app/templates/_do_targets.tpl @@ -50,6 +50,12 @@ Create a merged list of environment variables for delegated-operator templates secretKeyRef: name: {{ .secretName }} key: encryptionKey +{{- if and .ctx .ctx.Values.telemetry.enabled }} +- name: FIFTYONE_TELEMETRY_REDIS_URL + value: {{ printf "redis://%s:6379" (include "telemetry.redis.name" .ctx) | quote }} +- name: TELEMETRY_SOCKET + value: /tmp/telemetry/agent.sock +{{- end }} {{- range $key, $val := .env }} - name: {{ $key }} value: {{ $val | quote }} diff --git a/helm/fiftyone-teams-app/templates/_helpers.tpl b/helm/fiftyone-teams-app/templates/_helpers.tpl index bf4a311cb..8b5e1ac9f 100644 --- a/helm/fiftyone-teams-app/templates/_helpers.tpl +++ b/helm/fiftyone-teams-app/templates/_helpers.tpl @@ -324,6 +324,12 @@ Create a merged list of environment variables for delegated-operator-executor secretKeyRef: name: {{ .secretName }} key: encryptionKey +{{- if and .ctx .ctx.Values.telemetry.enabled }} +- name: FIFTYONE_TELEMETRY_REDIS_URL + value: {{ printf "redis://%s:6379" (include "telemetry.redis.name" .ctx) | quote }} +- name: TELEMETRY_SOCKET + value: /tmp/telemetry/agent.sock +{{- end }} {{- range $key, $val := .env }} - name: {{ $key }} value: {{ $val | quote }} @@ -382,6 +388,7 @@ Create a merged list of environment variables for fiftyone-teams-api secretKeyRef: name: {{ $secretName }} key: fiftyoneDatabaseName +{{- include "telemetry.redis-url-env" . }} {{- range $key, $val := .Values.apiSettings.env }} - name: {{ $key }} value: {{ $val | quote }} @@ -427,6 +434,7 @@ Create a merged list of environment variables for fiftyone-app secretKeyRef: name: {{ $secretName }} key: encryptionKey +{{- include "telemetry.redis-url-env" . }} {{- range $key, $val := .Values.appSettings.env }} - name: {{ $key }} value: {{ $val | quote }} @@ -543,6 +551,7 @@ Create a merged list of environment variables for fiftyone-teams-plugins secretKeyRef: name: {{ $secretName }} key: encryptionKey +{{- include "telemetry.redis-url-env" . }} {{- range $key, $val := .Values.pluginsSettings.env }} - name: {{ $key }} value: {{ $val | quote }} @@ -594,6 +603,7 @@ Create a merged list of environment variables for fiftyone-teams-app {{- else }} value: {{ printf "http://%s:%.0f" .Values.appSettings.service.name (float64 .Values.appSettings.service.port) | quote }} {{- end }} +{{- include "telemetry.redis-url-env" . }} {{- range $key, $val := .Values.teamsAppSettings.env }} - name: {{ $key }} value: {{ $val | quote }} diff --git a/helm/fiftyone-teams-app/templates/_telemetry.tpl b/helm/fiftyone-teams-app/templates/_telemetry.tpl index 6ebe5248e..632f1eb0b 100644 --- a/helm/fiftyone-teams-app/templates/_telemetry.tpl +++ b/helm/fiftyone-teams-app/templates/_telemetry.tpl @@ -38,3 +38,157 @@ app.kubernetes.io/name: telemetry app.kubernetes.io/instance: {{ .Release.Name }} app.voxel51.com/component: telemetry {{- end }} + +{{/* +Default subjects for the telemetry RoleBinding. When .Values.telemetry.serviceAccounts +is empty, bind to both the main app service account and the teams-api RBAC service +account (covering the SAs used by the auto-injected sidecars). +*/}} +{{- define "telemetry.role.subjects" -}} +{{- $appSA := include "fiftyone-teams-app.serviceAccountName" . | trim -}} +{{- $apiSA := include "teams-api-rbac.service-account-name" . | trim -}} +{{- $defaultSubjects := list $appSA $apiSA | uniq -}} +{{- $subjects := .Values.telemetry.serviceAccounts | default $defaultSubjects -}} +{{- range $subjects }} +- kind: ServiceAccount + name: {{ . }} + namespace: {{ $.Values.namespace.name }} +{{- end }} +{{- end }} + +{{/* +The shared base env vars for any telemetry-sidecar container. +Inputs (dict): + ctx — root context (.) + serviceType — value for SERVICE_TYPE env var (e.g. "teams-api") + targetName — value for TARGET_NAME env var (e.g. "fiftyone-teams-api") + podName — value for POD_NAME env var (defaults to fieldRef metadata.name) + executor — bool, when true emit EXECUTOR_SIDECAR=true and TELEMETRY_SOCKET env + targetContainer — when set, emit TARGET_CONTAINER env var (used by job sidecars) +*/}} +{{- define "telemetry.sidecar-env" -}} +{{- $secretName := .ctx.Values.secret.name -}} +- name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name +- name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace +- name: SERVICE_TYPE + value: {{ .serviceType | quote }} +- name: TARGET_NAME + value: {{ .targetName | quote }} +{{- if .targetContainer }} +- name: TARGET_CONTAINER + value: {{ .targetContainer | quote }} +{{- end }} +{{- if .executor }} +- name: EXECUTOR_SIDECAR + value: "true" +- name: TELEMETRY_SOCKET + value: /tmp/telemetry/agent.sock +{{- end }} +- name: FIFTYONE_TELEMETRY_REDIS_URL + value: {{ printf "redis://%s:6379" (include "telemetry.redis.name" .ctx) | quote }} +- name: FIFTYONE_DATABASE_URI + valueFrom: + secretKeyRef: + name: {{ $secretName }} + key: mongodbConnectionString +- name: FIFTYONE_DATABASE_NAME + valueFrom: + secretKeyRef: + name: {{ $secretName }} + key: fiftyoneDatabaseName +{{- end }} + +{{/* +A regular spec.containers entry: telemetry-sidecar for api/app/plugins/DO deployments. +Inputs: same dict as telemetry.sidecar-env. +*/}} +{{- define "telemetry.sidecar" -}} +- name: telemetry-sidecar + image: {{ .ctx.Values.telemetry.sidecar.image | default "voxel51/telemetry-sidecar:latest" | quote }} + imagePullPolicy: {{ .ctx.Values.telemetry.sidecar.imagePullPolicy | default "Always" }} + env: + {{- include "telemetry.sidecar-env" . | nindent 4 }} + {{- with .ctx.Values.telemetry.sidecar.resources }} + resources: + {{- toYaml . | nindent 4 }} + {{- end }} + securityContext: + capabilities: + add: + - SYS_PTRACE + {{- if .executor }} + volumeMounts: + - name: telemetry-socket + mountPath: /tmp/telemetry + {{- end }} +{{- end }} + +{{/* +A native-sidecar (initContainer with restartPolicy: Always) variant — for use under +spec.initContainers, primarily in DO Jobs where a regular extraContainer that does +not exit would block Job completion. +*/}} +{{- define "telemetry.native-sidecar" -}} +- name: telemetry-sidecar + image: {{ .ctx.Values.telemetry.sidecar.image | default "voxel51/telemetry-sidecar:latest" | quote }} + imagePullPolicy: {{ .ctx.Values.telemetry.sidecar.imagePullPolicy | default "Always" }} + restartPolicy: Always + env: + {{- include "telemetry.sidecar-env" . | nindent 4 }} + {{- with .ctx.Values.telemetry.sidecar.resources }} + resources: + {{- toYaml . | nindent 4 }} + {{- end }} + readinessProbe: + exec: + command: + - sh + - -c + - test -S /tmp/telemetry/agent.sock + failureThreshold: 30 + periodSeconds: 1 + securityContext: + capabilities: + add: + - SYS_PTRACE + volumeMounts: + - name: telemetry-socket + mountPath: /tmp/telemetry +{{- end }} + +{{/* +Emit a `FIFTYONE_TELEMETRY_REDIS_URL` env entry for a main workload container. +Renders empty when telemetry is disabled, allowing safe inclusion from +env-vars-list helpers. +*/}} +{{- define "telemetry.redis-url-env" -}} +{{- if .Values.telemetry.enabled }} +- name: FIFTYONE_TELEMETRY_REDIS_URL + value: {{ printf "redis://%s:6379" (include "telemetry.redis.name" .) | quote }} +{{- end }} +{{- end }} + +{{/* +Emit the `telemetry-socket` shared emptyDir volume entry for spec.volumes. +Used by DO deployments and DO jobs so the sidecar's unix socket can be +reached by both the executor and the sidecar. +*/}} +{{- define "telemetry.socket-volume" -}} +- name: telemetry-socket + emptyDir: {} +{{- end }} + +{{/* +Emit the `telemetry-socket` volumeMount for a workload container that needs to +reach the sidecar's unix socket. Pair with telemetry.socket-volume. +*/}} +{{- define "telemetry.socket-volume-mount" -}} +- name: telemetry-socket + mountPath: /tmp/telemetry +{{- end }} diff --git a/helm/fiftyone-teams-app/templates/api-deployment.yaml b/helm/fiftyone-teams-app/templates/api-deployment.yaml index 002f6f660..37635c293 100644 --- a/helm/fiftyone-teams-app/templates/api-deployment.yaml +++ b/helm/fiftyone-teams-app/templates/api-deployment.yaml @@ -44,7 +44,7 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ $serviceAccountName }} - {{- if or .Values.apiSettings.extraContainers .Values.apiSettings.nativeSidecarContainers }} + {{- if or .Values.apiSettings.extraContainers .Values.apiSettings.nativeSidecarContainers .Values.telemetry.enabled }} shareProcessNamespace: true {{- end }} securityContext: @@ -95,6 +95,9 @@ spec: {{- with .Values.apiSettings.extraContainers }} {{- toYaml . | nindent 8 }} {{- end }} + {{- if .Values.telemetry.enabled }} + {{- include "telemetry.sidecar" (dict "ctx" . "serviceType" "teams-api" "targetName" "fiftyone-teams-api") | nindent 8 }} + {{- end }} {{- if or .Values.apiSettings.initContainers.enabled .Values.apiSettings.nativeSidecarContainers }} initContainers: {{- if .Values.apiSettings.initContainers.enabled }} diff --git a/helm/fiftyone-teams-app/templates/app-deployment.yaml b/helm/fiftyone-teams-app/templates/app-deployment.yaml index 91d3f9d0c..b071e0a13 100644 --- a/helm/fiftyone-teams-app/templates/app-deployment.yaml +++ b/helm/fiftyone-teams-app/templates/app-deployment.yaml @@ -35,7 +35,7 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ include "fiftyone-teams-app.serviceAccountName" . }} - {{- if or .Values.appSettings.extraContainers .Values.appSettings.nativeSidecarContainers }} + {{- if or .Values.appSettings.extraContainers .Values.appSettings.nativeSidecarContainers .Values.telemetry.enabled }} shareProcessNamespace: true {{- end }} securityContext: @@ -79,6 +79,9 @@ spec: {{- with .Values.appSettings.extraContainers }} {{- toYaml . | nindent 8 }} {{- end }} + {{- if .Values.telemetry.enabled }} + {{- include "telemetry.sidecar" (dict "ctx" . "serviceType" "fiftyone-app" "targetName" "hypercorn") | nindent 8 }} + {{- end }} {{- if or .Values.appSettings.initContainers.enabled .Values.appSettings.nativeSidecarContainers }} initContainers: {{- if .Values.appSettings.initContainers.enabled }} diff --git a/helm/fiftyone-teams-app/templates/delegated-operator-instance-deployment.yaml b/helm/fiftyone-teams-app/templates/delegated-operator-instance-deployment.yaml index ab196e367..a628858d4 100644 --- a/helm/fiftyone-teams-app/templates/delegated-operator-instance-deployment.yaml +++ b/helm/fiftyone-teams-app/templates/delegated-operator-instance-deployment.yaml @@ -11,6 +11,7 @@ "apiServicePort" (float64 $.Values.apiSettings.service.port) "env" (merge (dict) ($v.env | default dict) ($baseTpl.env)) "secretEnv" (merge (dict) ($v.secretEnv | default dict) ($baseTpl.secretEnv)) + "ctx" $ ) }} {{- $defaultDescription := printf "Long running operations delegated to %s" $name }} @@ -50,7 +51,7 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ include "fiftyone-teams-app.serviceAccountName" $ }} - {{- if or ($v.extraContainers | default $baseTpl.extraContainers) ($v.nativeSidecarContainers | default $baseTpl.nativeSidecarContainers) }} + {{- if or ($v.extraContainers | default $baseTpl.extraContainers) ($v.nativeSidecarContainers | default $baseTpl.nativeSidecarContainers) $.Values.telemetry.enabled }} shareProcessNamespace: true {{- end }} securityContext: @@ -78,7 +79,17 @@ spec: {{- include "delegated-operator-deployments.env-vars-list" $envContext | indent 12 }} resources: {{- toYaml (merge (dict) ($v.resources | default dict) ($baseTpl.resources)) | nindent 12 }} - {{- with $v.volumeMounts | default $baseTpl.volumeMounts }} + {{- $mergedVolumeMounts := $v.volumeMounts | default $baseTpl.volumeMounts | default list }} + {{- if $.Values.telemetry.enabled }} + {{- $hasSocketMount := false }} + {{- range $vm := $mergedVolumeMounts }} + {{- if eq $vm.name "telemetry-socket" }}{{- $hasSocketMount = true }}{{- end }} + {{- end }} + {{- if not $hasSocketMount }} + {{- $mergedVolumeMounts = append $mergedVolumeMounts (dict "name" "telemetry-socket" "mountPath" "/tmp/telemetry") }} + {{- end }} + {{- end }} + {{- with $mergedVolumeMounts }} volumeMounts: {{- toYaml . | nindent 12 }} {{- end }} @@ -112,6 +123,9 @@ spec: {{- with $v.extraContainers | default $baseTpl.extraContainers }} {{- toYaml . | nindent 8 }} {{- end }} + {{- if $.Values.telemetry.enabled }} + {{- include "telemetry.sidecar" (dict "ctx" $ "serviceType" "delegated-operator" "targetName" "fiftyone delegated" "executor" true) | nindent 8 }} + {{- end }} {{- with $v.nativeSidecarContainers | default $baseTpl.nativeSidecarContainers }} initContainers: {{- toYaml . | nindent 8 }} @@ -139,7 +153,17 @@ spec: ) | nindent 8 }} {{- end }} - {{- with $v.volumes | default $baseTpl.volumes }} + {{- $mergedVolumes := $v.volumes | default $baseTpl.volumes | default list }} + {{- if $.Values.telemetry.enabled }} + {{- $hasSocketVol := false }} + {{- range $vol := $mergedVolumes }} + {{- if eq $vol.name "telemetry-socket" }}{{- $hasSocketVol = true }}{{- end }} + {{- end }} + {{- if not $hasSocketVol }} + {{- $mergedVolumes = append $mergedVolumes (dict "name" "telemetry-socket" "emptyDir" dict) }} + {{- end }} + {{- end }} + {{- with $mergedVolumes }} volumes: {{- toYaml . | nindent 8 }} {{- end }} diff --git a/helm/fiftyone-teams-app/templates/delegated-operator-job-configmap.yaml b/helm/fiftyone-teams-app/templates/delegated-operator-job-configmap.yaml index b611a83fd..adf148452 100644 --- a/helm/fiftyone-teams-app/templates/delegated-operator-job-configmap.yaml +++ b/helm/fiftyone-teams-app/templates/delegated-operator-job-configmap.yaml @@ -24,6 +24,7 @@ data: "apiServicePort" (float64 $.Values.apiSettings.service.port) "env" (merge (deepCopy ($jobConfig.env | default dict)) ($baseTpl.env)) "secretEnv" (merge (deepCopy ($jobConfig.secretEnv | default dict)) ($baseTpl.secretEnv)) + "ctx" $ }} {{- /* label context for helpers. */ -}} @@ -43,7 +44,21 @@ data: {{- $mergedVolumeMounts := $jobConfig.volumeMounts | default $baseTpl.volumeMounts }} {{- $mergedVolumes := $jobConfig.volumes | default $baseTpl.volumes }} {{- $mergedExtraContainers := $jobConfig.extraContainers | default $baseTpl.extraContainers }} - {{- $mergedNativeSidecarContainers := $jobConfig.nativeSidecarContainers | default $baseTpl.nativeSidecarContainers }} + {{- $mergedNativeSidecarContainers := $jobConfig.nativeSidecarContainers | default $baseTpl.nativeSidecarContainers | default list }} + + {{- /* Auto-inject the telemetry native-sidecar when telemetry is enabled + and the consumer hasn't supplied one of their own. */ -}} + {{- if $.Values.telemetry.enabled }} + {{- $hasTelemetrySidecar := false }} + {{- range $sc := $mergedNativeSidecarContainers }} + {{- if eq $sc.name "telemetry-sidecar" }}{{- $hasTelemetrySidecar = true }}{{- end }} + {{- end }} + {{- if not $hasTelemetrySidecar }} + {{- $telemetrySidecarYaml := include "telemetry.native-sidecar" (dict "ctx" $ "serviceType" "delegated-operator" "targetName" "fiftyone delegated" "targetContainer" "executor" "executor" true) }} + {{- $telemetrySidecar := $telemetrySidecarYaml | fromYamlArray | first }} + {{- $mergedNativeSidecarContainers = append $mergedNativeSidecarContainers $telemetrySidecar }} + {{- end }} + {{- end }} {{- $hasExecutorSidecar := false }} {{- range $sidecar := $mergedNativeSidecarContainers }} diff --git a/helm/fiftyone-teams-app/templates/plugins-deployment.yaml b/helm/fiftyone-teams-app/templates/plugins-deployment.yaml index 5524ae2ad..baa9762e9 100644 --- a/helm/fiftyone-teams-app/templates/plugins-deployment.yaml +++ b/helm/fiftyone-teams-app/templates/plugins-deployment.yaml @@ -36,7 +36,7 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ include "fiftyone-teams-app.serviceAccountName" . }} - {{- if or .Values.pluginsSettings.extraContainers .Values.pluginsSettings.nativeSidecarContainers }} + {{- if or .Values.pluginsSettings.extraContainers .Values.pluginsSettings.nativeSidecarContainers .Values.telemetry.enabled }} shareProcessNamespace: true {{- end }} securityContext: @@ -80,6 +80,9 @@ spec: {{- with .Values.pluginsSettings.extraContainers }} {{- toYaml . | nindent 8 }} {{- end }} + {{- if .Values.telemetry.enabled }} + {{- include "telemetry.sidecar" (dict "ctx" . "serviceType" "teams-plugins" "targetName" "hypercorn") | nindent 8 }} + {{- end }} {{- if or .Values.pluginsSettings.initContainers.enabled .Values.pluginsSettings.nativeSidecarContainers }} initContainers: {{- if .Values.pluginsSettings.initContainers.enabled }} diff --git a/helm/fiftyone-teams-app/templates/telemetry-rolebinding.yaml b/helm/fiftyone-teams-app/templates/telemetry-rolebinding.yaml index e148ae7db..8075f0d42 100644 --- a/helm/fiftyone-teams-app/templates/telemetry-rolebinding.yaml +++ b/helm/fiftyone-teams-app/templates/telemetry-rolebinding.yaml @@ -26,9 +26,5 @@ roleRef: kind: Role name: {{ include "telemetry.role.name" . }} subjects: -{{- range .Values.telemetry.serviceAccounts | default (list "default") }} - - kind: ServiceAccount - name: {{ . }} - namespace: {{ $.Values.namespace.name }} -{{- end }} + {{- include "telemetry.role.subjects" . | nindent 2 }} {{- end }} diff --git a/helm/fiftyone-teams-app/values.schema.json b/helm/fiftyone-teams-app/values.schema.json index ebcc62a3f..d5a735687 100644 --- a/helm/fiftyone-teams-app/values.schema.json +++ b/helm/fiftyone-teams-app/values.schema.json @@ -4010,11 +4010,11 @@ "type": "object" }, "telemetry": { - "description": "In-cluster telemetry support. When enabled, the chart renders a small\nRedis Deployment + Service (used by `telemetry-sidecar` containers\nconfigured on workload pods via `extraContainers`) plus a Role/RoleBinding\ngranting the sidecar's service accounts read access to `pods/log`.\nThe chart itself does NOT inject `telemetry-sidecar` containers; consumers\nwire those into `extraContainers` on the relevant workloads and point the\n`FIFTYONE_TELEMETRY_REDIS_URL` env var at this service.", + "description": "In-cluster telemetry. When enabled (the default), the chart renders:\n - a Redis Deployment + Service that collects per-service metric streams\n - a Role + RoleBinding granting the sidecar read access to `pods/log`\n - a `telemetry-sidecar` container auto-injected into the\n `teams-api`, `fiftyone-app`, `teams-plugins`, and delegated-operator\n workloads, plus a `nativeSidecarContainers` entry on the\n delegated-operator Job template\n - `FIFTYONE_TELEMETRY_REDIS_URL` on every workload that talks to the\n redis backend (api, app, plugins, teams-app, delegated operators)\n\nSet `enabled: false` to skip all telemetry resources and sidecar injection.", "properties": { "enabled": { "default": true, - "description": "Whether to render the telemetry Redis Deployment/Service and the\ntelemetry pod-logs Role/RoleBinding. Defaults to `true` so consumers\nthat wire up `telemetry-sidecar` containers get a working redis without\nadditional opt-in. Set to `false` to skip rendering all telemetry-related\nresources.", + "description": "Master switch. When `true` (default), all telemetry resources and\nauto-injected sidecars are rendered. When `false`, none are.", "title": "enabled", "type": "boolean" }, @@ -4092,12 +4092,73 @@ "type": "object" }, "serviceAccounts": { - "description": "ServiceAccount names (in `namespace.name`) bound to the telemetry\npod-logs Role. Each entry becomes a `ServiceAccount` subject on the\ngenerated RoleBinding. When empty, the RoleBinding subjects default to\nthe `default` ServiceAccount.", + "description": "ServiceAccount names (in `namespace.name`) bound to the telemetry\npod-logs Role. Each entry becomes a `ServiceAccount` subject on the\ngenerated RoleBinding. When empty, the RoleBinding subjects default to\nthe chart's main app service account and the teams-api RBAC service\naccount so auto-injected sidecars can read their target's logs.", "items": { "required": [] }, "title": "serviceAccounts", "type": "array" + }, + "sidecar": { + "properties": { + "image": { + "default": "voxel51/telemetry-sidecar:latest", + "description": "Container image for the auto-injected `telemetry-sidecar` containers.", + "title": "image", + "type": "string" + }, + "imagePullPolicy": { + "default": "Always", + "description": "Pull policy for the sidecar image.", + "title": "imagePullPolicy", + "type": "string" + }, + "resources": { + "description": "Resource requests/limits for each auto-injected sidecar container.", + "properties": { + "limits": { + "properties": { + "cpu": { + "default": "100m", + "title": "cpu", + "type": "string" + }, + "memory": { + "default": "512Mi", + "title": "memory", + "type": "string" + } + }, + "required": [], + "title": "limits", + "type": "object" + }, + "requests": { + "properties": { + "cpu": { + "default": "100m", + "title": "cpu", + "type": "string" + }, + "memory": { + "default": "512Mi", + "title": "memory", + "type": "string" + } + }, + "required": [], + "title": "requests", + "type": "object" + } + }, + "required": [], + "title": "resources", + "type": "object" + } + }, + "required": [], + "title": "sidecar", + "type": "object" } }, "required": [], diff --git a/helm/fiftyone-teams-app/values.yaml b/helm/fiftyone-teams-app/values.yaml index e5f65f64e..00b1ad466 100644 --- a/helm/fiftyone-teams-app/values.yaml +++ b/helm/fiftyone-teams-app/values.yaml @@ -1643,19 +1643,20 @@ teamsAppSettings: # -- Volumes for `teams-app` pods. [Reference][volumes]. volumes: [] -# In-cluster telemetry support. When enabled, the chart renders a small -# Redis Deployment + Service (used by `telemetry-sidecar` containers -# configured on workload pods via `extraContainers`) plus a Role/RoleBinding -# granting the sidecar's service accounts read access to `pods/log`. -# The chart itself does NOT inject `telemetry-sidecar` containers; consumers -# wire those into `extraContainers` on the relevant workloads and point the -# `FIFTYONE_TELEMETRY_REDIS_URL` env var at this service. +# In-cluster telemetry. When enabled (the default), the chart renders: +# - a Redis Deployment + Service that collects per-service metric streams +# - a Role + RoleBinding granting the sidecar read access to `pods/log` +# - a `telemetry-sidecar` container auto-injected into the +# `teams-api`, `fiftyone-app`, `teams-plugins`, and delegated-operator +# workloads, plus a `nativeSidecarContainers` entry on the +# delegated-operator Job template +# - `FIFTYONE_TELEMETRY_REDIS_URL` on every workload that talks to the +# redis backend (api, app, plugins, teams-app, delegated operators) +# +# Set `enabled: false` to skip all telemetry resources and sidecar injection. telemetry: - # -- Whether to render the telemetry Redis Deployment/Service and the - # telemetry pod-logs Role/RoleBinding. Defaults to `true` so consumers - # that wire up `telemetry-sidecar` containers get a working redis without - # additional opt-in. Set to `false` to skip rendering all telemetry-related - # resources. + # -- Master switch. When `true` (default), all telemetry resources and + # auto-injected sidecars are rendered. When `false`, none are. enabled: true redis: # -- Container image for the telemetry Redis Deployment. @@ -1674,8 +1675,22 @@ telemetry: limits: cpu: 250m memory: 512Mi + sidecar: + # -- Container image for the auto-injected `telemetry-sidecar` containers. + image: voxel51/telemetry-sidecar:latest + # -- Pull policy for the sidecar image. + imagePullPolicy: Always + # -- Resource requests/limits for each auto-injected sidecar container. + resources: + requests: + cpu: 100m + memory: 512Mi + limits: + cpu: 100m + memory: 512Mi # -- ServiceAccount names (in `namespace.name`) bound to the telemetry # pod-logs Role. Each entry becomes a `ServiceAccount` subject on the # generated RoleBinding. When empty, the RoleBinding subjects default to - # the `default` ServiceAccount. + # the chart's main app service account and the teams-api RBAC service + # account so auto-injected sidecars can read their target's logs. serviceAccounts: [] diff --git a/tests/unit/helm/api-deployment_test.go b/tests/unit/helm/api-deployment_test.go index 30c37c935..bf7b66861 100644 --- a/tests/unit/helm/api-deployment_test.go +++ b/tests/unit/helm/api-deployment_test.go @@ -96,7 +96,7 @@ func (s *deploymentApiTemplateTest) TestMetadataLabels() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -137,7 +137,7 @@ func (s *deploymentApiTemplateTest) TestMetadataName() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -175,7 +175,7 @@ func (s *deploymentApiTemplateTest) TestMetadataNamespace() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -221,7 +221,7 @@ func (s *deploymentApiTemplateTest) TestReplicas() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -380,7 +380,7 @@ func (s *deploymentApiTemplateTest) TestTopologySpreadConstraints() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -399,9 +399,14 @@ func (s *deploymentApiTemplateTest) TestContainerCount() { }{ { "defaultValues", - nil, + map[string]string{"telemetry.enabled": "false"}, 1, }, + { + "telemetryEnabled", + map[string]string{"telemetry.enabled": "true"}, + 2, // main + telemetry-sidecar + }, } for _, testCase := range testCases { @@ -411,7 +416,7 @@ func (s *deploymentApiTemplateTest) TestContainerCount() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1164,7 +1169,7 @@ func (s *deploymentApiTemplateTest) TestContainerEnv() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1226,7 +1231,7 @@ func (s *deploymentApiTemplateTest) TestContainerImage() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1264,7 +1269,7 @@ func (s *deploymentApiTemplateTest) TestContainerImagePullPolicy() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1303,7 +1308,7 @@ func (s *deploymentApiTemplateTest) TestContainerName() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1392,7 +1397,7 @@ func (s *deploymentApiTemplateTest) TestContainerLivenessProbe() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1455,7 +1460,7 @@ func (s *deploymentApiTemplateTest) TestContainerPorts() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1544,7 +1549,7 @@ func (s *deploymentApiTemplateTest) TestContainerReadinessProbe() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1633,7 +1638,7 @@ func (s *deploymentApiTemplateTest) TestContainerStartupProbe() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1691,7 +1696,7 @@ func (s *deploymentApiTemplateTest) TestContainerResourceRequirements() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1745,7 +1750,7 @@ func (s *deploymentApiTemplateTest) TestContainerSecurityContext() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1865,7 +1870,7 @@ func (s *deploymentApiTemplateTest) TestContainerVolumeMounts() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1903,7 +1908,7 @@ func (s *deploymentApiTemplateTest) TestInitContainerCount() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1942,7 +1947,7 @@ func (s *deploymentApiTemplateTest) TestInitContainerImage() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1994,7 +1999,7 @@ func (s *deploymentApiTemplateTest) TestInitContainerCommand() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2067,7 +2072,7 @@ func (s *deploymentApiTemplateTest) TestInitContainerResourceRequirements() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2128,7 +2133,7 @@ func (s *deploymentApiTemplateTest) TestInitContainerSecurityContext() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2195,7 +2200,7 @@ func (s *deploymentApiTemplateTest) TestAffinity() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2233,7 +2238,7 @@ func (s *deploymentApiTemplateTest) TestImagePullSecrets() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2277,7 +2282,7 @@ func (s *deploymentApiTemplateTest) TestNodeSelector() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2320,7 +2325,7 @@ func (s *deploymentApiTemplateTest) TestDeploymentAnnotations() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2367,7 +2372,7 @@ func (s *deploymentApiTemplateTest) TestPodAnnotations() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2429,7 +2434,7 @@ func (s *deploymentApiTemplateTest) TestPodSecurityContext() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2504,7 +2509,7 @@ func (s *deploymentApiTemplateTest) TestTemplateLabels() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2575,7 +2580,7 @@ func (s *deploymentApiTemplateTest) TestServiceAccountName() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2631,7 +2636,7 @@ func (s *deploymentApiTemplateTest) TestTolerations() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2787,7 +2792,7 @@ func (s *deploymentApiTemplateTest) TestVolumes() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2860,7 +2865,7 @@ func (s *deploymentApiTemplateTest) TestDeploymentUpdateStrategy() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment diff --git a/tests/unit/helm/app-deployment_test.go b/tests/unit/helm/app-deployment_test.go index bfb1b9298..3f3cfba20 100644 --- a/tests/unit/helm/app-deployment_test.go +++ b/tests/unit/helm/app-deployment_test.go @@ -94,7 +94,7 @@ func (s *deploymentAppTemplateTest) TestMetadataLabels() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -135,7 +135,7 @@ func (s *deploymentAppTemplateTest) TestMetadataName() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -173,7 +173,7 @@ func (s *deploymentAppTemplateTest) TestMetadataNamespace() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -211,7 +211,7 @@ func (s *deploymentAppTemplateTest) TestReplicas() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -370,7 +370,7 @@ func (s *deploymentAppTemplateTest) TestTopologySpreadConstraints() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -401,7 +401,7 @@ func (s *deploymentAppTemplateTest) TestContainerCount() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -912,7 +912,7 @@ func (s *deploymentAppTemplateTest) TestContainerEnv() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -974,7 +974,7 @@ func (s *deploymentAppTemplateTest) TestContainerImage() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1012,7 +1012,7 @@ func (s *deploymentAppTemplateTest) TestContainerImagePullPolicy() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1051,7 +1051,7 @@ func (s *deploymentAppTemplateTest) TestContainerName() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1137,7 +1137,7 @@ func (s *deploymentAppTemplateTest) TestContainerLivenessProbe() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1200,7 +1200,7 @@ func (s *deploymentAppTemplateTest) TestContainerPorts() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1286,7 +1286,7 @@ func (s *deploymentAppTemplateTest) TestContainerReadinessProbe() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1372,7 +1372,7 @@ func (s *deploymentAppTemplateTest) TestContainerStartupProbe() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1430,7 +1430,7 @@ func (s *deploymentAppTemplateTest) TestContainerResourceRequirements() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1484,7 +1484,7 @@ func (s *deploymentAppTemplateTest) TestContainerSecurityContext() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1561,7 +1561,7 @@ func (s *deploymentAppTemplateTest) TestContainerVolumeMounts() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1599,7 +1599,7 @@ func (s *deploymentAppTemplateTest) TestInitContainerCount() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1638,7 +1638,7 @@ func (s *deploymentAppTemplateTest) TestInitContainerImage() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1690,7 +1690,7 @@ func (s *deploymentAppTemplateTest) TestInitContainerCommand() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1763,7 +1763,7 @@ func (s *deploymentAppTemplateTest) TestInitContainerResourceRequirements() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1824,7 +1824,7 @@ func (s *deploymentAppTemplateTest) TestInitContainerSecurityContext() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1891,7 +1891,7 @@ func (s *deploymentAppTemplateTest) TestAffinity() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1929,7 +1929,7 @@ func (s *deploymentAppTemplateTest) TestImagePullSecrets() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1973,7 +1973,7 @@ func (s *deploymentAppTemplateTest) TestNodeSelector() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2016,7 +2016,7 @@ func (s *deploymentAppTemplateTest) TestDeploymentAnnotations() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2063,7 +2063,7 @@ func (s *deploymentAppTemplateTest) TestPodAnnotations() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2125,7 +2125,7 @@ func (s *deploymentAppTemplateTest) TestPodSecurityContext() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2194,7 +2194,7 @@ func (s *deploymentAppTemplateTest) TestTemplateLabels() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2242,7 +2242,7 @@ func (s *deploymentAppTemplateTest) TestServiceAccountName() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2298,7 +2298,7 @@ func (s *deploymentAppTemplateTest) TestTolerations() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2381,7 +2381,7 @@ func (s *deploymentAppTemplateTest) TestVolumes() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2454,7 +2454,7 @@ func (s *deploymentAppTemplateTest) TestDeploymentUpdateStrategy() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment diff --git a/tests/unit/helm/delegated-operator-instance-deployment_test.go b/tests/unit/helm/delegated-operator-instance-deployment_test.go index ba744ea5e..118a4dbdf 100644 --- a/tests/unit/helm/delegated-operator-instance-deployment_test.go +++ b/tests/unit/helm/delegated-operator-instance-deployment_test.go @@ -82,7 +82,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestDisabled() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} if testCase.expected == nil { output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) @@ -200,7 +200,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestMetadataLabels() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} if testCase.expected == nil { output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) @@ -276,7 +276,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestMetadataName() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} if testCase.expected == nil { output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -358,7 +358,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestMetadataNamespace( subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} if testCase.expected == nil { output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) @@ -370,7 +370,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestMetadataNamespace( s.Empty(deployment.ObjectMeta.Namespace, "Metadata namespace should be nil") } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) @@ -455,7 +455,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestReplicas() { subT.Parallel() if testCase.expected == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -465,7 +465,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestReplicas() { s.Empty(&deployment.Spec.Replicas, "Replica count should be nil.") } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) // https://github.com/gruntwork-io/terratest/issues/586#issuecomment-848542351 @@ -782,7 +782,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestTopologySpreadCons subT.Parallel() if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -792,7 +792,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestTopologySpreadCons s.Empty(deployment.Spec.Template.Spec.TopologySpreadConstraints, "Topology constraints should be nil") } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) @@ -845,7 +845,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerCount() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} if testCase.expected == nil { output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) @@ -1697,7 +1697,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerEnv() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -1707,7 +1707,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerEnv() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) // https://github.com/gruntwork-io/terratest/issues/586#issuecomment-848542351 @@ -1901,7 +1901,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerImage() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -1911,7 +1911,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerImage() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) // https://github.com/gruntwork-io/terratest/issues/586#issuecomment-848542351 @@ -2001,7 +2001,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerImagePull // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -2011,7 +2011,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerImagePull s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) // https://github.com/gruntwork-io/terratest/issues/586#issuecomment-848542351 @@ -2074,7 +2074,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerName() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -2084,7 +2084,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerName() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) // https://github.com/gruntwork-io/terratest/issues/586#issuecomment-848542351 @@ -2425,7 +2425,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerResourceR subT.Parallel() // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -2435,7 +2435,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerResourceR s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) // https://github.com/gruntwork-io/terratest/issues/586#issuecomment-848542351 @@ -2581,7 +2581,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerSecurityC // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -2592,7 +2592,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerSecurityC s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) // https://github.com/gruntwork-io/terratest/issues/586#issuecomment-848542351 @@ -2800,7 +2800,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerVolumeMou // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -2811,7 +2811,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerVolumeMou s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) // https://github.com/gruntwork-io/terratest/issues/586#issuecomment-848542351 @@ -3127,9 +3127,9 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestAffinity() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -3209,7 +3209,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestImagePullSecrets() // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -3220,7 +3220,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestImagePullSecrets() s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) // https://github.com/gruntwork-io/terratest/issues/586#issuecomment-848542351 @@ -3332,7 +3332,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestNodeSelector() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -3342,7 +3342,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestNodeSelector() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) // https://github.com/gruntwork-io/terratest/issues/586#issuecomment-848542351 @@ -3462,7 +3462,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestDeploymentAnnotati // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -3473,7 +3473,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestDeploymentAnnotati s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) // https://github.com/gruntwork-io/terratest/issues/586#issuecomment-848542351 allRange := strings.Split(output, "---") @@ -3594,7 +3594,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestPodAnnotations() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -3604,7 +3604,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestPodAnnotations() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) // https://github.com/gruntwork-io/terratest/issues/586#issuecomment-848542351 allRange := strings.Split(output, "---") @@ -3767,7 +3767,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestPodSecurityContext // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -3777,7 +3777,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestPodSecurityContext s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) // https://github.com/gruntwork-io/terratest/issues/586#issuecomment-848542351 @@ -3955,7 +3955,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestTemplateLabels() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -3966,7 +3966,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestTemplateLabels() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) // https://github.com/gruntwork-io/terratest/issues/586#issuecomment-848542351 @@ -4038,7 +4038,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestServiceAccountName subT.Parallel() if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -4048,7 +4048,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestServiceAccountName s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) // https://github.com/gruntwork-io/terratest/issues/586#issuecomment-848542351 @@ -4227,7 +4227,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestTolerations() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -4238,7 +4238,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestTolerations() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) // https://github.com/gruntwork-io/terratest/issues/586#issuecomment-848542351 @@ -4451,7 +4451,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestVolumes() { subT.Parallel() if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -4462,7 +4462,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestVolumes() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { // when vars are set outside of the if statement, they aren't accessible from within the conditional - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) // https://github.com/gruntwork-io/terratest/issues/586#issuecomment-848542351 @@ -4754,7 +4754,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerLivenessP subT.Parallel() if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -4765,7 +4765,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerLivenessP s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) // https://github.com/gruntwork-io/terratest/issues/586#issuecomment-848542351 @@ -5007,7 +5007,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerReadiness subT := s.T() subT.Parallel() if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -5017,7 +5017,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerReadiness s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) // https://github.com/gruntwork-io/terratest/issues/586#issuecomment-848542351 @@ -5262,7 +5262,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerStartupPr subT := s.T() subT.Parallel() if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -5272,7 +5272,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerStartupPr s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) // https://github.com/gruntwork-io/terratest/issues/586#issuecomment-848542351 @@ -5472,7 +5472,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerCmdArgs() subT.Parallel() if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/delegated-operator-instance-deployment.yaml in chart") @@ -5482,7 +5482,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestContainerCmdArgs() s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) // https://github.com/gruntwork-io/terratest/issues/586#issuecomment-848542351 @@ -5659,7 +5659,7 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestDeploymentUpdateSt subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} if testCase.values == nil { diff --git a/tests/unit/helm/delegated-operator-job-configmap_test.go b/tests/unit/helm/delegated-operator-job-configmap_test.go index 0199a13cc..dcbb97d5c 100644 --- a/tests/unit/helm/delegated-operator-job-configmap_test.go +++ b/tests/unit/helm/delegated-operator-job-configmap_test.go @@ -89,7 +89,7 @@ func (s *doK8sConfigMapTemplateTest) TestDisabled() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} if testCase.expected == "" { output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) @@ -138,7 +138,7 @@ func (s *doK8sConfigMapTemplateTest) TestMetadataName() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) @@ -177,7 +177,7 @@ func (s *doK8sConfigMapTemplateTest) TestMetadataNamespace() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) @@ -218,7 +218,7 @@ func (s *doK8sConfigMapTemplateTest) TestMetadataAnnotations() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var configMap corev1.ConfigMap @@ -291,7 +291,7 @@ func (s *doK8sConfigMapTemplateTest) TestMetadataLabels() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var configMap corev1.ConfigMap @@ -638,7 +638,7 @@ func (s *doK8sConfigMapTemplateTest) TestData() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) @@ -742,7 +742,7 @@ func (s *doK8sConfigMapTemplateTest) TestNativeSidecarTelemetryAutoInject() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values, SetStrValues: testCase.strValues} + options := &helm.Options{SetValues: disableTelemetry(testCase.values), SetStrValues: testCase.strValues} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var configMap corev1.ConfigMap diff --git a/tests/unit/helm/plugins-deployment_test.go b/tests/unit/helm/plugins-deployment_test.go index 72ba89406..459679ce8 100644 --- a/tests/unit/helm/plugins-deployment_test.go +++ b/tests/unit/helm/plugins-deployment_test.go @@ -102,7 +102,7 @@ func (s *deploymentPluginsTemplateTest) TestMetadataLabels() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} if testCase.expected == nil { output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) @@ -162,7 +162,7 @@ func (s *deploymentPluginsTemplateTest) TestMetadataName() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} if testCase.expected == "" { output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) @@ -220,7 +220,7 @@ func (s *deploymentPluginsTemplateTest) TestMetadataNamespace() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} if testCase.expected == "" { output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) @@ -278,7 +278,7 @@ func (s *deploymentPluginsTemplateTest) TestReplicas() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} if testCase.expected == 0 { output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -452,7 +452,7 @@ func (s *deploymentPluginsTemplateTest) TestTopologySpreadConstraints() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -490,7 +490,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerCount() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} if testCase.expected == 0 { output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) @@ -971,7 +971,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerEnv() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -981,7 +981,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerEnv() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1056,7 +1056,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerImage() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -1066,7 +1066,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerImage() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1115,7 +1115,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerImagePullPolicy() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -1125,7 +1125,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerImagePullPolicy() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1174,7 +1174,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerName() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -1184,7 +1184,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerName() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1284,7 +1284,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerLivenessProbe() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -1294,7 +1294,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerLivenessProbe() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1370,7 +1370,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerPorts() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -1380,7 +1380,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerPorts() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1480,7 +1480,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerReadinessProbe() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -1490,7 +1490,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerReadinessProbe() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1590,7 +1590,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerStartupProbe() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -1600,7 +1600,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerStartupProbe() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1671,7 +1671,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerResourceRequirements() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -1681,7 +1681,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerResourceRequirements() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1748,7 +1748,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerSecurityContext() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -1758,7 +1758,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerSecurityContext() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1849,7 +1849,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerVolumeMounts() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -1859,7 +1859,7 @@ func (s *deploymentPluginsTemplateTest) TestContainerVolumeMounts() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1902,7 +1902,7 @@ func (s *deploymentPluginsTemplateTest) TestInitContainerCount() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1944,7 +1944,7 @@ func (s *deploymentPluginsTemplateTest) TestInitContainerImage() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1999,7 +1999,7 @@ func (s *deploymentPluginsTemplateTest) TestInitContainerCommand() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2075,7 +2075,7 @@ func (s *deploymentPluginsTemplateTest) TestInitContainerResourceRequirements() subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2139,7 +2139,7 @@ func (s *deploymentPluginsTemplateTest) TestInitContainerSecurityContext() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2218,7 +2218,7 @@ func (s *deploymentPluginsTemplateTest) TestAffinity() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -2228,7 +2228,7 @@ func (s *deploymentPluginsTemplateTest) TestAffinity() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2277,7 +2277,7 @@ func (s *deploymentPluginsTemplateTest) TestImagePullSecrets() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -2287,7 +2287,7 @@ func (s *deploymentPluginsTemplateTest) TestImagePullSecrets() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2342,7 +2342,7 @@ func (s *deploymentPluginsTemplateTest) TestNodeSelector() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -2352,7 +2352,7 @@ func (s *deploymentPluginsTemplateTest) TestNodeSelector() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2406,7 +2406,7 @@ func (s *deploymentPluginsTemplateTest) TestDeploymentAnnotations() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -2416,7 +2416,7 @@ func (s *deploymentPluginsTemplateTest) TestDeploymentAnnotations() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2474,7 +2474,7 @@ func (s *deploymentPluginsTemplateTest) TestPodAnnotations() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -2484,7 +2484,7 @@ func (s *deploymentPluginsTemplateTest) TestPodAnnotations() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2559,7 +2559,7 @@ func (s *deploymentPluginsTemplateTest) TestPodSecurityContext() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -2569,7 +2569,7 @@ func (s *deploymentPluginsTemplateTest) TestPodSecurityContext() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2650,7 +2650,7 @@ func (s *deploymentPluginsTemplateTest) TestTemplateLabels() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -2660,7 +2660,7 @@ func (s *deploymentPluginsTemplateTest) TestTemplateLabels() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2718,7 +2718,7 @@ func (s *deploymentPluginsTemplateTest) TestServiceAccountName() { subT.Parallel() if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -2728,7 +2728,7 @@ func (s *deploymentPluginsTemplateTest) TestServiceAccountName() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2797,7 +2797,7 @@ func (s *deploymentPluginsTemplateTest) TestTolerations() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -2807,7 +2807,7 @@ func (s *deploymentPluginsTemplateTest) TestTolerations() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2904,7 +2904,7 @@ func (s *deploymentPluginsTemplateTest) TestVolumes() { // when vars are set outside of the if statement, they aren't accessible from within the conditional if testCase.values == nil { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output, err := helm.RenderTemplateE(subT, options, s.chartPath, s.releaseName, s.templates) s.ErrorContains(err, "could not find template templates/plugins-deployment.yaml in chart") @@ -2914,7 +2914,7 @@ func (s *deploymentPluginsTemplateTest) TestVolumes() { s.Nil(deployment.Spec.Template.Spec.Containers) } else { - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -3000,7 +3000,7 @@ func (s *deploymentPluginsTemplateTest) TestDeploymentUpdateStrategy() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} var deployment appsv1.Deployment if testCase.values == nil { diff --git a/tests/unit/helm/teams-app-deployment_test.go b/tests/unit/helm/teams-app-deployment_test.go index 9a51fad57..e1c96eff9 100644 --- a/tests/unit/helm/teams-app-deployment_test.go +++ b/tests/unit/helm/teams-app-deployment_test.go @@ -97,7 +97,7 @@ func (s *deploymentTeamsAppTemplateTest) TestMetadataLabels() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -138,7 +138,7 @@ func (s *deploymentTeamsAppTemplateTest) TestMetadataName() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -176,7 +176,7 @@ func (s *deploymentTeamsAppTemplateTest) TestMetadataNamespace() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -214,7 +214,7 @@ func (s *deploymentTeamsAppTemplateTest) TestReplicas() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -373,7 +373,7 @@ func (s *deploymentTeamsAppTemplateTest) TestTopologySpreadConstraints() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -404,7 +404,7 @@ func (s *deploymentTeamsAppTemplateTest) TestContainerCount() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1046,7 +1046,7 @@ func (s *deploymentTeamsAppTemplateTest) TestContainerEnv() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1108,7 +1108,7 @@ func (s *deploymentTeamsAppTemplateTest) TestContainerImage() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1146,7 +1146,7 @@ func (s *deploymentTeamsAppTemplateTest) TestContainerImagePullPolicy() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1185,7 +1185,7 @@ func (s *deploymentTeamsAppTemplateTest) TestContainerName() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1274,7 +1274,7 @@ func (s *deploymentTeamsAppTemplateTest) TestContainerLivenessProbe() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1337,7 +1337,7 @@ func (s *deploymentTeamsAppTemplateTest) TestContainerPorts() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1426,7 +1426,7 @@ func (s *deploymentTeamsAppTemplateTest) TestContainerReadinessProbe() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1515,7 +1515,7 @@ func (s *deploymentTeamsAppTemplateTest) TestContainerStartupProbe() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1573,7 +1573,7 @@ func (s *deploymentTeamsAppTemplateTest) TestContainerResourceRequirements() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1627,7 +1627,7 @@ func (s *deploymentTeamsAppTemplateTest) TestContainerSecurityContext() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1704,7 +1704,7 @@ func (s *deploymentTeamsAppTemplateTest) TestContainerVolumeMounts() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1742,7 +1742,7 @@ func (s *deploymentTeamsAppTemplateTest) TestInitContainerCount() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1781,7 +1781,7 @@ func (s *deploymentTeamsAppTemplateTest) TestInitContainerImage() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1833,7 +1833,7 @@ func (s *deploymentTeamsAppTemplateTest) TestInitContainerCommand() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1906,7 +1906,7 @@ func (s *deploymentTeamsAppTemplateTest) TestInitContainerResourceRequirements() subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -1967,7 +1967,7 @@ func (s *deploymentTeamsAppTemplateTest) TestInitContainerSecurityContext() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2034,7 +2034,7 @@ func (s *deploymentTeamsAppTemplateTest) TestAffinity() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2072,7 +2072,7 @@ func (s *deploymentTeamsAppTemplateTest) TestImagePullSecrets() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2116,7 +2116,7 @@ func (s *deploymentTeamsAppTemplateTest) TestNodeSelector() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2159,7 +2159,7 @@ func (s *deploymentTeamsAppTemplateTest) TestDeploymentAnnotations() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2206,7 +2206,7 @@ func (s *deploymentTeamsAppTemplateTest) TestPodAnnotations() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2268,7 +2268,7 @@ func (s *deploymentTeamsAppTemplateTest) TestPodSecurityContext() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2339,7 +2339,7 @@ func (s *deploymentTeamsAppTemplateTest) TestTemplateLabels() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2387,7 +2387,7 @@ func (s *deploymentTeamsAppTemplateTest) TestServiceAccountName() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2443,7 +2443,7 @@ func (s *deploymentTeamsAppTemplateTest) TestTolerations() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2526,7 +2526,7 @@ func (s *deploymentTeamsAppTemplateTest) TestVolumes() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment @@ -2599,7 +2599,7 @@ func (s *deploymentTeamsAppTemplateTest) TestDeploymentUpdateStrategy() { subT := s.T() subT.Parallel() - options := &helm.Options{SetValues: testCase.values} + options := &helm.Options{SetValues: disableTelemetry(testCase.values)} output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) var deployment appsv1.Deployment diff --git a/tests/unit/helm/telemetry-rolebinding_test.go b/tests/unit/helm/telemetry-rolebinding_test.go index 7b052daf4..024bb4757 100644 --- a/tests/unit/helm/telemetry-rolebinding_test.go +++ b/tests/unit/helm/telemetry-rolebinding_test.go @@ -130,9 +130,9 @@ func (s *telemetryRoleBindingTemplateTest) TestRoleRules() { } func (s *telemetryRoleBindingTemplateTest) TestRoleBindingDefaultSubject() { - // With an unset serviceAccounts list, the binding should fall back to - // a single subject pointing at the "default" ServiceAccount in the - // chart namespace. + // With an unset serviceAccounts list, the binding falls back to the + // chart's main app service account and the teams-api RBAC service + // account (the SAs used by auto-injected sidecars). options := &helm.Options{SetValues: map[string]string{ "telemetry.enabled": "true", }} @@ -141,10 +141,19 @@ func (s *telemetryRoleBindingTemplateTest) TestRoleBindingDefaultSubject() { rb, ok := s.extractRoleBinding(output) s.Require().True(ok, "RoleBinding document not found in rendered output") - s.Require().Len(rb.Subjects, 1, "Default RoleBinding should have exactly one subject") - s.Equal("ServiceAccount", rb.Subjects[0].Kind) - s.Equal("default", rb.Subjects[0].Name) - s.Equal("fiftyone-teams", rb.Subjects[0].Namespace) + s.Require().Len(rb.Subjects, 2, "Default RoleBinding should bind to app + api-rbac SAs") + subjectNames := map[string]bool{} + for _, sub := range rb.Subjects { + s.Equal("ServiceAccount", sub.Kind) + s.Equal("fiftyone-teams", sub.Namespace) + subjectNames[sub.Name] = true + } + // Main app SA: chart default serviceAccount.name = "fiftyone-teams" + mainAppSA := "fiftyone-teams" + // teams-api SA: -teams-api + apiSA := fmt.Sprintf("%s-fiftyone-teams-app-teams-api", s.releaseName) + s.True(subjectNames[mainAppSA], "should bind to main app SA %q", mainAppSA) + s.True(subjectNames[apiSA], "should bind to teams-api SA %q", apiSA) } func (s *telemetryRoleBindingTemplateTest) TestRoleBindingMultipleSubjects() { diff --git a/tests/unit/helm/yaml_helpers.go b/tests/unit/helm/yaml_helpers.go index 05e13c0e7..de1a1c8df 100644 --- a/tests/unit/helm/yaml_helpers.go +++ b/tests/unit/helm/yaml_helpers.go @@ -15,3 +15,17 @@ func splitYAMLDocs(s string) []string { } return out } + +// disableTelemetry returns a copy of the given helm SetValues map with +// `telemetry.enabled=false` injected as a default. Caller-supplied keys +// (including `telemetry.enabled=true`) win over the default. Use this in +// pre-existing tests that assert non-telemetry deployment shape so the +// chart's new `telemetry.enabled=true` default doesn't add a sidecar +// container / env var / volume that breaks their expectations. +func disableTelemetry(values map[string]string) map[string]string { + out := map[string]string{"telemetry.enabled": "false"} + for k, v := range values { + out[k] = v + } + return out +} From 8fc3ca4f24734efaefa42902c2fd38b71d0c4b17 Mon Sep 17 00:00:00 2001 From: kacey Date: Tue, 19 May 2026 12:14:05 -0700 Subject: [PATCH 19/23] test: temporarily hardcode the sidecar --- helm/fiftyone-teams-app/README.md | 2 +- .../templates/_telemetry.tpl | 4 +-- helm/fiftyone-teams-app/values.schema.json | 4 +-- helm/fiftyone-teams-app/values.yaml | 5 +++- utils/validate-docker-pulls.sh | 29 +++++++++++++++++-- 5 files changed, 36 insertions(+), 8 deletions(-) diff --git a/helm/fiftyone-teams-app/README.md b/helm/fiftyone-teams-app/README.md index 294a4e786..8ab38cecb 100644 --- a/helm/fiftyone-teams-app/README.md +++ b/helm/fiftyone-teams-app/README.md @@ -1101,7 +1101,7 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): | telemetry.redis.resources | object | `{"limits":{"cpu":"250m","memory":"512Mi"},"requests":{"cpu":"100m","memory":"256Mi"}}` | Resource requests/limits for the telemetry Redis container. [Reference][resources]. | | telemetry.redis.storage | string | `"1Gi"` | Persistent volume size for the telemetry Redis PVC. | | telemetry.serviceAccounts | list | `[]` | ServiceAccount names (in `namespace.name`) bound to the telemetry pod-logs Role. Each entry becomes a `ServiceAccount` subject on the generated RoleBinding. When empty, the RoleBinding subjects default to the chart's main app service account and the teams-api RBAC service account so auto-injected sidecars can read their target's logs. | -| telemetry.sidecar.image | string | `"voxel51/telemetry-sidecar:latest"` | Container image for the auto-injected `telemetry-sidecar` containers. | +| telemetry.sidecar.image | string | `"us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-telemetry-sidecar:v0.1.61"` | Container image for the auto-injected `telemetry-sidecar` containers. The image is built and published from voxel51/fiftyone-teams `sidecar/` to the internal Voxel51 GAR. Override for environments without access to that registry (e.g. external chart consumers). | | telemetry.sidecar.imagePullPolicy | string | `"Always"` | Pull policy for the sidecar image. | | telemetry.sidecar.resources | object | `{"limits":{"cpu":"100m","memory":"512Mi"},"requests":{"cpu":"100m","memory":"512Mi"}}` | Resource requests/limits for each auto-injected sidecar container. | diff --git a/helm/fiftyone-teams-app/templates/_telemetry.tpl b/helm/fiftyone-teams-app/templates/_telemetry.tpl index 632f1eb0b..f8dfd2602 100644 --- a/helm/fiftyone-teams-app/templates/_telemetry.tpl +++ b/helm/fiftyone-teams-app/templates/_telemetry.tpl @@ -110,7 +110,7 @@ Inputs: same dict as telemetry.sidecar-env. */}} {{- define "telemetry.sidecar" -}} - name: telemetry-sidecar - image: {{ .ctx.Values.telemetry.sidecar.image | default "voxel51/telemetry-sidecar:latest" | quote }} + image: {{ .ctx.Values.telemetry.sidecar.image | default "us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-telemetry-sidecar:v0.1.61" | quote }} imagePullPolicy: {{ .ctx.Values.telemetry.sidecar.imagePullPolicy | default "Always" }} env: {{- include "telemetry.sidecar-env" . | nindent 4 }} @@ -136,7 +136,7 @@ not exit would block Job completion. */}} {{- define "telemetry.native-sidecar" -}} - name: telemetry-sidecar - image: {{ .ctx.Values.telemetry.sidecar.image | default "voxel51/telemetry-sidecar:latest" | quote }} + image: {{ .ctx.Values.telemetry.sidecar.image | default "us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-telemetry-sidecar:v0.1.61" | quote }} imagePullPolicy: {{ .ctx.Values.telemetry.sidecar.imagePullPolicy | default "Always" }} restartPolicy: Always env: diff --git a/helm/fiftyone-teams-app/values.schema.json b/helm/fiftyone-teams-app/values.schema.json index d5a735687..6a691ddab 100644 --- a/helm/fiftyone-teams-app/values.schema.json +++ b/helm/fiftyone-teams-app/values.schema.json @@ -4102,8 +4102,8 @@ "sidecar": { "properties": { "image": { - "default": "voxel51/telemetry-sidecar:latest", - "description": "Container image for the auto-injected `telemetry-sidecar` containers.", + "default": "us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-telemetry-sidecar:v0.1.61", + "description": "Container image for the auto-injected `telemetry-sidecar` containers.\nThe image is built and published from voxel51/fiftyone-teams `sidecar/`\nto the internal Voxel51 GAR. Override for environments without access\nto that registry (e.g. external chart consumers).", "title": "image", "type": "string" }, diff --git a/helm/fiftyone-teams-app/values.yaml b/helm/fiftyone-teams-app/values.yaml index 00b1ad466..94257a320 100644 --- a/helm/fiftyone-teams-app/values.yaml +++ b/helm/fiftyone-teams-app/values.yaml @@ -1677,7 +1677,10 @@ telemetry: memory: 512Mi sidecar: # -- Container image for the auto-injected `telemetry-sidecar` containers. - image: voxel51/telemetry-sidecar:latest + # The image is built and published from voxel51/fiftyone-teams `sidecar/` + # to the internal Voxel51 GAR. Override for environments without access + # to that registry (e.g. external chart consumers). + image: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-telemetry-sidecar:v0.1.61 # -- Pull policy for the sidecar image. imagePullPolicy: Always # -- Resource requests/limits for each auto-injected sidecar container. diff --git a/utils/validate-docker-pulls.sh b/utils/validate-docker-pulls.sh index c830e09e5..d4e619b0a 100755 --- a/utils/validate-docker-pulls.sh +++ b/utils/validate-docker-pulls.sh @@ -14,6 +14,8 @@ set -euo pipefail # These are images we expect to be pullable in an # exhaustive set. +# Images with an explicit `:tag` are taken as-is; voxel51/ images without +# a tag get the chart appVersion appended automatically. EXPECTED_IMAGES=( "docker.io/busybox:stable-glibc" # For initContainers "docker.io/redis:7-alpine" # For telemetry redis (telemetry.enabled defaults to true) @@ -26,6 +28,21 @@ EXPECTED_IMAGES=( "voxel51/fiftyone-teams-cv-full" ) +# Images that the chart references by default but that are NOT publicly +# pullable. They bypass the docker_pull validation step but still count +# as "expected" for the in-helm-template check (so their presence doesn't +# trip the "in helm template but not in our published images" guard). +# Add the explicit `:tag` here — no auto-tag. +# +# Move entries OUT of this list once the corresponding image has a +# publicly-pullable tag (e.g. once the telemetry sidecar starts +# publishing to Docker Hub instead of the private Voxel51 GAR). +UNPUBLISHED_IMAGES=( + # Built from voxel51/fiftyone-teams `sidecar/`, published to the + # internal Voxel51 GAR. Requires GCP auth to pull. + "us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-telemetry-sidecar:v0.1.61" +) + . "$(dirname "$0")/common.sh" VALUES_YAML="${GIT_ROOT}/helm/fiftyone-teams-app/values.yaml" @@ -102,7 +119,11 @@ expected_images_with_tag=() expected_tag=$(yq '.appVersion' "${GIT_ROOT}/helm/fiftyone-teams-app/Chart.yaml") for img in "${EXPECTED_IMAGES[@]}"; do - if [[ ${img} =~ "voxel51/" ]]; then + if [[ ${img} == *":"* ]]; then + # Image already carries an explicit tag (e.g. telemetry-sidecar:latest); + # use as-is. + img_with_tag="${img}" + elif [[ ${img} =~ "voxel51/" ]]; then # Only add a tag for our organizations images, not # publicly available ones our chart may reference img_with_tag="${img}:${expected_tag}" @@ -123,12 +144,16 @@ helm_images=$( read -r -a helm_images_array <<<"${helm_images}" +# Images that are allowed in the helm template but skip docker_pull +# validation (e.g. not yet published to a public registry). +known_images_for_template_check=("${expected_images_with_tag[@]}" "${UNPUBLISHED_IMAGES[@]}") + pids=() rcs=() exit_code=0 for img in "${helm_images_array[@]}"; do - if [[ ! ${expected_images_with_tag[*]} =~ ${img} ]]; then + if [[ ! ${known_images_for_template_check[*]} =~ ${img} ]]; then echo "Image ${img} is in the 'helm template', but not in our published images!" exit 1 fi From bfce12b91554dd4d0f1a475f6533dc5daee9f702 Mon Sep 17 00:00:00 2001 From: kacey Date: Tue, 19 May 2026 12:18:31 -0700 Subject: [PATCH 20/23] fix: use latest sidecar version 0.1.62 --- helm/fiftyone-teams-app/README.md | 2 +- helm/fiftyone-teams-app/templates/_telemetry.tpl | 4 ++-- helm/fiftyone-teams-app/values.schema.json | 2 +- helm/fiftyone-teams-app/values.yaml | 2 +- utils/validate-docker-pulls.sh | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/helm/fiftyone-teams-app/README.md b/helm/fiftyone-teams-app/README.md index 8ab38cecb..dfe3250ac 100644 --- a/helm/fiftyone-teams-app/README.md +++ b/helm/fiftyone-teams-app/README.md @@ -1101,7 +1101,7 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): | telemetry.redis.resources | object | `{"limits":{"cpu":"250m","memory":"512Mi"},"requests":{"cpu":"100m","memory":"256Mi"}}` | Resource requests/limits for the telemetry Redis container. [Reference][resources]. | | telemetry.redis.storage | string | `"1Gi"` | Persistent volume size for the telemetry Redis PVC. | | telemetry.serviceAccounts | list | `[]` | ServiceAccount names (in `namespace.name`) bound to the telemetry pod-logs Role. Each entry becomes a `ServiceAccount` subject on the generated RoleBinding. When empty, the RoleBinding subjects default to the chart's main app service account and the teams-api RBAC service account so auto-injected sidecars can read their target's logs. | -| telemetry.sidecar.image | string | `"us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-telemetry-sidecar:v0.1.61"` | Container image for the auto-injected `telemetry-sidecar` containers. The image is built and published from voxel51/fiftyone-teams `sidecar/` to the internal Voxel51 GAR. Override for environments without access to that registry (e.g. external chart consumers). | +| telemetry.sidecar.image | string | `"us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-telemetry-sidecar:v0.1.62"` | Container image for the auto-injected `telemetry-sidecar` containers. The image is built and published from voxel51/fiftyone-teams `sidecar/` to the internal Voxel51 GAR. Override for environments without access to that registry (e.g. external chart consumers). | | telemetry.sidecar.imagePullPolicy | string | `"Always"` | Pull policy for the sidecar image. | | telemetry.sidecar.resources | object | `{"limits":{"cpu":"100m","memory":"512Mi"},"requests":{"cpu":"100m","memory":"512Mi"}}` | Resource requests/limits for each auto-injected sidecar container. | diff --git a/helm/fiftyone-teams-app/templates/_telemetry.tpl b/helm/fiftyone-teams-app/templates/_telemetry.tpl index f8dfd2602..4b91b88b7 100644 --- a/helm/fiftyone-teams-app/templates/_telemetry.tpl +++ b/helm/fiftyone-teams-app/templates/_telemetry.tpl @@ -110,7 +110,7 @@ Inputs: same dict as telemetry.sidecar-env. */}} {{- define "telemetry.sidecar" -}} - name: telemetry-sidecar - image: {{ .ctx.Values.telemetry.sidecar.image | default "us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-telemetry-sidecar:v0.1.61" | quote }} + image: {{ .ctx.Values.telemetry.sidecar.image | default "us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-telemetry-sidecar:v0.1.62" | quote }} imagePullPolicy: {{ .ctx.Values.telemetry.sidecar.imagePullPolicy | default "Always" }} env: {{- include "telemetry.sidecar-env" . | nindent 4 }} @@ -136,7 +136,7 @@ not exit would block Job completion. */}} {{- define "telemetry.native-sidecar" -}} - name: telemetry-sidecar - image: {{ .ctx.Values.telemetry.sidecar.image | default "us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-telemetry-sidecar:v0.1.61" | quote }} + image: {{ .ctx.Values.telemetry.sidecar.image | default "us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-telemetry-sidecar:v0.1.62" | quote }} imagePullPolicy: {{ .ctx.Values.telemetry.sidecar.imagePullPolicy | default "Always" }} restartPolicy: Always env: diff --git a/helm/fiftyone-teams-app/values.schema.json b/helm/fiftyone-teams-app/values.schema.json index 6a691ddab..7d68c3443 100644 --- a/helm/fiftyone-teams-app/values.schema.json +++ b/helm/fiftyone-teams-app/values.schema.json @@ -4102,7 +4102,7 @@ "sidecar": { "properties": { "image": { - "default": "us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-telemetry-sidecar:v0.1.61", + "default": "us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-telemetry-sidecar:v0.1.62", "description": "Container image for the auto-injected `telemetry-sidecar` containers.\nThe image is built and published from voxel51/fiftyone-teams `sidecar/`\nto the internal Voxel51 GAR. Override for environments without access\nto that registry (e.g. external chart consumers).", "title": "image", "type": "string" diff --git a/helm/fiftyone-teams-app/values.yaml b/helm/fiftyone-teams-app/values.yaml index 94257a320..08c6bc01d 100644 --- a/helm/fiftyone-teams-app/values.yaml +++ b/helm/fiftyone-teams-app/values.yaml @@ -1680,7 +1680,7 @@ telemetry: # The image is built and published from voxel51/fiftyone-teams `sidecar/` # to the internal Voxel51 GAR. Override for environments without access # to that registry (e.g. external chart consumers). - image: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-telemetry-sidecar:v0.1.61 + image: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-telemetry-sidecar:v0.1.62 # -- Pull policy for the sidecar image. imagePullPolicy: Always # -- Resource requests/limits for each auto-injected sidecar container. diff --git a/utils/validate-docker-pulls.sh b/utils/validate-docker-pulls.sh index d4e619b0a..40e7d8e20 100755 --- a/utils/validate-docker-pulls.sh +++ b/utils/validate-docker-pulls.sh @@ -40,7 +40,7 @@ EXPECTED_IMAGES=( UNPUBLISHED_IMAGES=( # Built from voxel51/fiftyone-teams `sidecar/`, published to the # internal Voxel51 GAR. Requires GCP auth to pull. - "us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-telemetry-sidecar:v0.1.61" + "us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-telemetry-sidecar:v0.1.62" ) . "$(dirname "$0")/common.sh" From 79bf6e3c9441eaf52cd42b6fd34504971c917135 Mon Sep 17 00:00:00 2001 From: voxelbot Date: Tue, 19 May 2026 20:09:48 +0000 Subject: [PATCH 21/23] ci(fixtures): automated bump for integration test fixtures teams-app:v2.19.0-rc.5 api:v2.19.0rc6 app:v2.19.0rc6 cas:v2.19.0-rc.5 --- .../fixtures/docker/integration_internal_auth.env | 10 +++++----- tests/fixtures/docker/integration_legacy_auth.env | 10 +++++----- tests/fixtures/helm/integration_values.yaml | 14 +++++++------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/fixtures/docker/integration_internal_auth.env b/tests/fixtures/docker/integration_internal_auth.env index 677024ad6..e9bbce260 100644 --- a/tests/fixtures/docker/integration_internal_auth.env +++ b/tests/fixtures/docker/integration_internal_auth.env @@ -31,10 +31,10 @@ FIFTYONE_ENV=development CAS_DEBUG="cas:*" # Image tags for existing artifacts -VERSION=v2.18.1 -FIFTYONE_APP_VERSION=v2.18.1 -FIFTYONE_TEAMS_API_VERSION=v2.18.1 -FIFTYONE_TEAMS_APP_VERSION=v2.18.1 -FIFTYONE_TEAMS_CAS_VERSION=v2.18.1 +VERSION=v2.19.0rc6 +FIFTYONE_APP_VERSION=v2.19.0rc6 +FIFTYONE_TEAMS_API_VERSION=v2.19.0rc6 +FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.5 +FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.5 FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS=1 diff --git a/tests/fixtures/docker/integration_legacy_auth.env b/tests/fixtures/docker/integration_legacy_auth.env index 1d77f397d..70b0c7463 100644 --- a/tests/fixtures/docker/integration_legacy_auth.env +++ b/tests/fixtures/docker/integration_legacy_auth.env @@ -39,10 +39,10 @@ FIFTYONE_ENV=development CAS_DEBUG="cas:*" # Image tags for existing artifacts -VERSION=v2.18.1 -FIFTYONE_APP_VERSION=v2.18.1 -FIFTYONE_TEAMS_API_VERSION=v2.18.1 -FIFTYONE_TEAMS_APP_VERSION=v2.18.1 -FIFTYONE_TEAMS_CAS_VERSION=v2.18.1 +VERSION=v2.19.0rc6 +FIFTYONE_APP_VERSION=v2.19.0rc6 +FIFTYONE_TEAMS_API_VERSION=v2.19.0rc6 +FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.5 +FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.5 FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS=1 diff --git a/tests/fixtures/helm/integration_values.yaml b/tests/fixtures/helm/integration_values.yaml index f66afa156..54796228e 100644 --- a/tests/fixtures/helm/integration_values.yaml +++ b/tests/fixtures/helm/integration_values.yaml @@ -9,7 +9,7 @@ apiSettings: # See https://console.cloud.google.com/artifacts/docker/computer-vision-team/us-central1/dev-docker?project=computer-vision-team repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-teams-api pullPolicy: IfNotPresent - tag: v2.18.1 + tag: v2.19.0rc6 service: liveness: initialDelaySeconds: 15 @@ -27,7 +27,7 @@ appSettings: # See https://console.cloud.google.com/artifacts/docker/computer-vision-team/us-central1/dev-docker?project=computer-vision-team repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.18.1 + tag: v2.19.0rc6 # TODO: Test `minikube addons configure registry-creds` or # When using minikube's addon registry-creds, we may also need to create the k8s secret `regcred` # See https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ @@ -42,21 +42,21 @@ casSettings: # See https://console.cloud.google.com/artifacts/docker/computer-vision-team/us-central1/dev-docker?project=computer-vision-team repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-teams-cas pullPolicy: IfNotPresent - tag: v2.18.1 + tag: v2.19.0-rc.5 delegatedOperatorDeployments: deployments: teamsDoCpuDefault: image: repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.18.1 + tag: v2.19.0rc6 delegatedOperatorTemplates: jobs: teamsDoCpuDefaultK8s: image: repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.18.1 + tag: v2.19.0rc6 ingress: annotations: # For using the nginx-ingress controller with cert-manager self signed certificates @@ -121,7 +121,7 @@ pluginsSettings: # See https://console.cloud.google.com/artifacts/docker/computer-vision-team/us-central1/dev-docker?project=computer-vision-team repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.18.1 + tag: v2.19.0rc6 teamsAppSettings: dnsName: local.fiftyone.ai # env: @@ -138,4 +138,4 @@ teamsAppSettings: # The others are `vW.X.Y.devZ` (note `.devZ` vs `-dev.Z`). # This is a byproduct of `npm` versioning versus Python PEP 440. pullPolicy: IfNotPresent - tag: v2.18.1 + tag: v2.19.0-rc.5 From 41fa1abc48c325ec4ef5dc6693bde3a843eaf444 Mon Sep 17 00:00:00 2001 From: kacey Date: Tue, 19 May 2026 13:34:34 -0700 Subject: [PATCH 22/23] fix: runAsNonRoot override in sidecar --- helm/fiftyone-teams-app/templates/_telemetry.tpl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/helm/fiftyone-teams-app/templates/_telemetry.tpl b/helm/fiftyone-teams-app/templates/_telemetry.tpl index 4b91b88b7..690d1e65c 100644 --- a/helm/fiftyone-teams-app/templates/_telemetry.tpl +++ b/helm/fiftyone-teams-app/templates/_telemetry.tpl @@ -119,6 +119,11 @@ Inputs: same dict as telemetry.sidecar-env. {{- toYaml . | nindent 4 }} {{- end }} securityContext: + # The sidecar image runs as root (SYS_PTRACE + /proc//fd/1 access + # require it). Explicit container-level override so it works even when + # the pod's podSecurityContext sets runAsNonRoot: true. + runAsNonRoot: false + runAsUser: 0 capabilities: add: - SYS_PTRACE @@ -154,6 +159,11 @@ not exit would block Job completion. failureThreshold: 30 periodSeconds: 1 securityContext: + # The sidecar image runs as root (SYS_PTRACE + /proc//fd/1 access + # require it). Explicit container-level override so it works even when + # the pod's podSecurityContext sets runAsNonRoot: true. + runAsNonRoot: false + runAsUser: 0 capabilities: add: - SYS_PTRACE From 72148c6d2f86244d8f2a57e5a8e0a931924a1a95 Mon Sep 17 00:00:00 2001 From: voxelbot Date: Tue, 19 May 2026 21:21:53 +0000 Subject: [PATCH 23/23] ci(fixtures): automated bump for integration test fixtures teams-app:v2.19.0-rc.6 api:v2.19.0rc7 app:v2.19.0rc7 cas:v2.19.0-rc.6 --- .../fixtures/docker/integration_internal_auth.env | 10 +++++----- tests/fixtures/docker/integration_legacy_auth.env | 10 +++++----- tests/fixtures/helm/integration_values.yaml | 14 +++++++------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/fixtures/docker/integration_internal_auth.env b/tests/fixtures/docker/integration_internal_auth.env index e9bbce260..d0919314e 100644 --- a/tests/fixtures/docker/integration_internal_auth.env +++ b/tests/fixtures/docker/integration_internal_auth.env @@ -31,10 +31,10 @@ FIFTYONE_ENV=development CAS_DEBUG="cas:*" # Image tags for existing artifacts -VERSION=v2.19.0rc6 -FIFTYONE_APP_VERSION=v2.19.0rc6 -FIFTYONE_TEAMS_API_VERSION=v2.19.0rc6 -FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.5 -FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.5 +VERSION=v2.19.0rc7 +FIFTYONE_APP_VERSION=v2.19.0rc7 +FIFTYONE_TEAMS_API_VERSION=v2.19.0rc7 +FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.6 +FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.6 FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS=1 diff --git a/tests/fixtures/docker/integration_legacy_auth.env b/tests/fixtures/docker/integration_legacy_auth.env index 70b0c7463..e833b77b8 100644 --- a/tests/fixtures/docker/integration_legacy_auth.env +++ b/tests/fixtures/docker/integration_legacy_auth.env @@ -39,10 +39,10 @@ FIFTYONE_ENV=development CAS_DEBUG="cas:*" # Image tags for existing artifacts -VERSION=v2.19.0rc6 -FIFTYONE_APP_VERSION=v2.19.0rc6 -FIFTYONE_TEAMS_API_VERSION=v2.19.0rc6 -FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.5 -FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.5 +VERSION=v2.19.0rc7 +FIFTYONE_APP_VERSION=v2.19.0rc7 +FIFTYONE_TEAMS_API_VERSION=v2.19.0rc7 +FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.6 +FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.6 FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS=1 diff --git a/tests/fixtures/helm/integration_values.yaml b/tests/fixtures/helm/integration_values.yaml index 54796228e..2c6973f65 100644 --- a/tests/fixtures/helm/integration_values.yaml +++ b/tests/fixtures/helm/integration_values.yaml @@ -9,7 +9,7 @@ apiSettings: # See https://console.cloud.google.com/artifacts/docker/computer-vision-team/us-central1/dev-docker?project=computer-vision-team repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-teams-api pullPolicy: IfNotPresent - tag: v2.19.0rc6 + tag: v2.19.0rc7 service: liveness: initialDelaySeconds: 15 @@ -27,7 +27,7 @@ appSettings: # See https://console.cloud.google.com/artifacts/docker/computer-vision-team/us-central1/dev-docker?project=computer-vision-team repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.19.0rc6 + tag: v2.19.0rc7 # TODO: Test `minikube addons configure registry-creds` or # When using minikube's addon registry-creds, we may also need to create the k8s secret `regcred` # See https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ @@ -42,21 +42,21 @@ casSettings: # See https://console.cloud.google.com/artifacts/docker/computer-vision-team/us-central1/dev-docker?project=computer-vision-team repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-teams-cas pullPolicy: IfNotPresent - tag: v2.19.0-rc.5 + tag: v2.19.0-rc.6 delegatedOperatorDeployments: deployments: teamsDoCpuDefault: image: repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.19.0rc6 + tag: v2.19.0rc7 delegatedOperatorTemplates: jobs: teamsDoCpuDefaultK8s: image: repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.19.0rc6 + tag: v2.19.0rc7 ingress: annotations: # For using the nginx-ingress controller with cert-manager self signed certificates @@ -121,7 +121,7 @@ pluginsSettings: # See https://console.cloud.google.com/artifacts/docker/computer-vision-team/us-central1/dev-docker?project=computer-vision-team repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.19.0rc6 + tag: v2.19.0rc7 teamsAppSettings: dnsName: local.fiftyone.ai # env: @@ -138,4 +138,4 @@ teamsAppSettings: # The others are `vW.X.Y.devZ` (note `.devZ` vs `-dev.Z`). # This is a byproduct of `npm` versioning versus Python PEP 440. pullPolicy: IfNotPresent - tag: v2.19.0-rc.5 + tag: v2.19.0-rc.6