From 78d5f0d09219e505856715dc4fb25c440ca7959c Mon Sep 17 00:00:00 2001 From: Tom Schmidt Date: Wed, 13 May 2026 11:25:14 -0700 Subject: [PATCH 01/10] chore: bump release 2.19.0 Co-Authored-By: Claude Opus 4.7 (1M context) --- Makefile | 2 +- docker/README.md | 2 +- docker/common-services.yaml | 14 +++---- docker/docs/configuring-gpu-workloads.md | 2 +- docker/docs/upgrading.md | 38 +++++++++---------- docs/custom-plugins.md | 4 +- .../configuring-databricks-orchestrator.md | 2 +- helm/docs/upgrading.md | 36 +++++++++--------- helm/fiftyone-teams-app/Chart.yaml | 4 +- helm/fiftyone-teams-app/README.md | 4 +- helm/fiftyone-teams-app/values.schema.json | 2 +- helm/fiftyone-teams-app/values.yaml | 2 +- helm/values.yaml | 2 +- skaffold.yaml | 12 +++--- .../docker-compose-internal-auth_test.go | 18 ++++----- .../docker-compose-legacy-auth_test.go | 20 +++++----- 16 files changed, 82 insertions(+), 82 deletions(-) diff --git a/Makefile b/Makefile index 4d55b8a0..ac323afa 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ SHELL := $(SHELL) -e ASDF := $(shell asdf where golang) -VERSION ?= 2.18.1 +VERSION ?= 2.19.0 # Help .PHONY: $(shell sed -n -e '/^$$/ { n ; /^[^ .\#][^ ]*:/ { s/:.*$$// ; p ; } ; }' $(MAKEFILE_LIST)) diff --git a/docker/README.md b/docker/README.md index e97cd60e..7f231589 100644 --- a/docker/README.md +++ b/docker/README.md @@ -239,7 +239,7 @@ You can override the default image used by any service in ```yaml services: fiftyone-app: - image: voxel51/fiftyone-app-torch:v2.18.1 + image: voxel51/fiftyone-app-torch:v2.19.0 ``` > [!NOTE] diff --git a/docker/common-services.yaml b/docker/common-services.yaml index 7050831d..bc71259a 100644 --- a/docker/common-services.yaml +++ b/docker/common-services.yaml @@ -1,7 +1,7 @@ --- services: fiftyone-app-common: - image: voxel51/fiftyone-app:v2.18.1 + image: voxel51/fiftyone-app:v2.19.0 environment: API_URL: ${API_URL} FIFTYONE_APP_DEFAULT_QUERY_PERFORMANCE: ${FIFTYONE_APP_DEFAULT_QUERY_PERFORMANCE} @@ -32,7 +32,7 @@ services: restart: always teams-api-common: - image: voxel51/fiftyone-teams-api:v2.18.1 + image: voxel51/fiftyone-teams-api:v2.19.0 environment: API_EXTERNAL_URL: ${FIFTYONE_API_URI} CAS_BASE_URL: ${CAS_BASE_URL:-http://teams-cas:3000/cas/api} @@ -61,13 +61,13 @@ services: restart: always teams-app-common: - image: voxel51/fiftyone-teams-app:v2.18.1 + image: voxel51/fiftyone-teams-app:v2.19.0 environment: API_URL: ${API_URL} APP_USE_HTTPS: ${APP_USE_HTTPS:-true} FIFTYONE_API_URI: ${FIFTYONE_API_URI:-"Please contact your Admin for an API URI"} FIFTYONE_APP_ALLOW_MEDIA_EXPORT: ${FIFTYONE_APP_ALLOW_MEDIA_EXPORT:-true} - FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION: 2.18.1 + FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION: 2.19.0 FIFTYONE_AUTH_SECRET: ${FIFTYONE_AUTH_SECRET} FIFTYONE_SERVER_ADDRESS: "" FIFTYONE_SERVER_PATH_PREFIX: /api/proxy/fiftyone-teams @@ -95,7 +95,7 @@ services: restart: always teams-cas-common: - image: voxel51/fiftyone-teams-cas:v2.18.1 + image: voxel51/fiftyone-teams-cas:v2.19.0 environment: CAS_DATABASE_NAME: ${CAS_DATABASE_NAME:-cas} CAS_DEFAULT_USER_ROLE: ${CAS_DEFAULT_USER_ROLE:-GUEST} @@ -130,7 +130,7 @@ services: read_only: true teams-plugins-common: - image: voxel51/fiftyone-app-torch:v2.18.1 + image: voxel51/fiftyone-app-torch:v2.19.0 environment: API_URL: ${API_URL} FIFTYONE_AUTH_SECRET: ${FIFTYONE_AUTH_SECRET} @@ -157,7 +157,7 @@ services: restart: always teams-do-common: - image: voxel51/fiftyone-teams-cv-full:v2.18.1 + image: voxel51/fiftyone-teams-cv-full:v2.19.0 deploy: replicas: ${FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS:-3} command: > diff --git a/docker/docs/configuring-gpu-workloads.md b/docker/docs/configuring-gpu-workloads.md index 9fa1fcf8..1e4b12f5 100644 --- a/docker/docs/configuring-gpu-workloads.md +++ b/docker/docs/configuring-gpu-workloads.md @@ -65,7 +65,7 @@ services: service: teams-do-common teams-do-with-gpu: - image: voxel51/fiftyone-teams-cv-full:v2.18.1 + image: voxel51/fiftyone-teams-cv-full:v2.19.0 command: > /bin/sh -c "fiftyone delegated launch -t remote -n 'teams-do-with-gpu' -m" environment: diff --git a/docker/docs/upgrading.md b/docker/docs/upgrading.md index 585cd0b8..61cf8037 100644 --- a/docker/docs/upgrading.md +++ b/docker/docs/upgrading.md @@ -80,9 +80,9 @@ quickstart 0.21.2 ### From FiftyOne Enterprise Version 2.0.0 and Later -1. [Upgrade to FiftyOne Enterprise version 2.18.1](#upgrading-from-previous-versions) +1. [Upgrade to FiftyOne Enterprise version 2.19.0](#upgrading-from-previous-versions) 1. Voxel51 recommends upgrading all FiftyOne Enterprise SDK users to FiftyOne Enterprise - version 2.18.1 + version 2.19.0 1. Login to the FiftyOne Enterprise UI 1. To obtain the CLI command to install the FiftyOne SDK associated with your FiftyOne Enterprise version, navigate to `Account > Install FiftyOne` @@ -175,7 +175,7 @@ To utilize the prior image, update your `common-services.yaml` similar to the be ```yaml teams-do-common: - image: voxel51/fiftyone-app:v2.18.1 + image: voxel51/fiftyone-app:v2.19.0 ``` #### FiftyOne Enterprise v2.2+ Delegated Operator Changes @@ -210,7 +210,7 @@ Additionally, ### From FiftyOne Enterprise Versions 1.6.0 to 1.7.1 -> **NOTE**: Upgrading to FiftyOne Enterprise v2.18.1 _requires_ a license file. +> **NOTE**: Upgrading to FiftyOne Enterprise v2.19.0 _requires_ a license file. > Please contact your Customer Success Team before upgrading to FiftyOne Enterprise > 2.0 or beyond. > @@ -245,15 +245,15 @@ Additionally, mv license.key "${LOCAL_LICENSE_FILE_DIR}/license" ``` -1. [Upgrade to FiftyOne Enterprise version 2.18.1](#upgrading-from-previous-versions) -1. Upgrade FiftyOne Enterprise SDK users to FiftyOne Enterprise version 2.18.1 +1. [Upgrade to FiftyOne Enterprise version 2.19.0](#upgrading-from-previous-versions) +1. Upgrade FiftyOne Enterprise SDK users to FiftyOne Enterprise version 2.19.0 1. Login to the FiftyOne Enterprise UI 1. To obtain the CLI command to install the FiftyOne SDK associated with your FiftyOne Enterprise version, navigate to `Account > Install FiftyOne` 1. Upgrade all the datasets - > **NOTE**: Any FiftyOne SDK less than 2.18.1 + > **NOTE**: Any FiftyOne SDK less than 2.19.0 > will lose connectivity at this point. - > Upgrading to `fiftyone==2.18.1` is required. + > Upgrading to `fiftyone==2.19.0` is required. ```shell FIFTYONE_DATABASE_ADMIN=true fiftyone migrate --all @@ -267,7 +267,7 @@ Additionally, ### From FiftyOne Enterprise Version 1.1.0 and Before Version 1.6.0 -> **NOTE**: Upgrading to FiftyOne Enterprise v2.18.1 _requires_ +> **NOTE**: Upgrading to FiftyOne Enterprise v2.19.0 _requires_ > your users to log in after the upgrade is complete. > This will interrupt active workflows in the FiftyOne Enterprise Hosted Web App. > You should coordinate this upgrade carefully with your end-users. @@ -285,7 +285,7 @@ Additionally, --- -> **NOTE**: Upgrading to FiftyOne Enterprise v2.18.1 _requires_ a license file. +> **NOTE**: Upgrading to FiftyOne Enterprise v2.19.0 _requires_ a license file. > Please contact your Customer Success Team before upgrading to FiftyOne Enterprise > 2.0 or beyond. > @@ -337,15 +337,15 @@ Additionally, unset FIFTYONE_DATABASE_ADMIN ``` -1. [Upgrade to FiftyOne Enterprise version 2.18.1](#upgrading-from-previous-versions) -1. Upgrade FiftyOne Enterprise SDK users to FiftyOne Enterprise version 2.18.1 +1. [Upgrade to FiftyOne Enterprise version 2.19.0](#upgrading-from-previous-versions) +1. Upgrade FiftyOne Enterprise SDK users to FiftyOne Enterprise version 2.19.0 1. Login to the FiftyOne Enterprise UI 1. To obtain the CLI command to install the FiftyOne SDK associated with your FiftyOne Enterprise version, navigate to `Account > Install FiftyOne` 1. Upgrade all the datasets - > **NOTE**: Any FiftyOne SDK less than 2.18.1 + > **NOTE**: Any FiftyOne SDK less than 2.19.0 > will lose connectivity at this point. - > Upgrading to `fiftyone==2.18.1` is required. + > Upgrading to `fiftyone==2.19.0` is required. ```shell FIFTYONE_DATABASE_ADMIN=true fiftyone migrate --all @@ -376,14 +376,14 @@ Additionally, --- -> **NOTE**: Upgrading to FiftyOne Enterprise v2.18.1 _requires_ your users to +> **NOTE**: Upgrading to FiftyOne Enterprise v2.19.0 _requires_ your users to > log in after the upgrade is complete. > This will interrupt active workflows in the FiftyOne Enterprise Hosted Web App. > You should coordinate this upgrade carefully with your end-users. --- -> **NOTE**: Upgrading to FiftyOne Enterprise v2.18.1 _requires_ a license file. +> **NOTE**: Upgrading to FiftyOne Enterprise v2.19.0 _requires_ a license file. > Please contact your Customer Success Team before upgrading to FiftyOne Enterprise > 2.0 or beyond. > @@ -414,13 +414,13 @@ Additionally, 1. Update your web server routes to include routing `/cas/*` traffic to the `teams-cas` service. Please see our [example nginx configurations](../) for more information. -1. [Upgrade to FiftyOne Enterprise v2.18.1](#upgrading-from-previous-versions) +1. [Upgrade to FiftyOne Enterprise v2.19.0](#upgrading-from-previous-versions) with `FIFTYONE_DATABASE_ADMIN=true` (this is not the default for this release). > **NOTE**: FiftyOne SDK users will lose access to the FiftyOne - > Enterprise Database at this step until they upgrade to `fiftyone==2.18.1` + > Enterprise Database at this step until they upgrade to `fiftyone==2.19.0` -1. Upgrade your FiftyOne SDKs to version 2.18.1 +1. Upgrade your FiftyOne SDKs to version 2.19.0 1. Login to the FiftyOne Enterprise UI 1. To obtain the CLI command to install the FiftyOne SDK associated with your FiftyOne Enterprise version, navigate to diff --git a/docs/custom-plugins.md b/docs/custom-plugins.md index fdb64ed9..95cad5b5 100644 --- a/docs/custom-plugins.md +++ b/docs/custom-plugins.md @@ -46,7 +46,7 @@ With a Dockerfile like this, you could use the following commands to build, and publish, your image to your internal registry ```shell -FIFTYONE_ENTERPRISE_VERSION=v2.18.1 +FIFTYONE_ENTERPRISE_VERSION=v2.19.0 docker buildx build --push \ --build-arg FIFTYONE_ENTERPRISE_IMAGE_NAME="voxel51/fiftyone-app:${FIFTYONE_ENTERPRISE_VERSION}" \ -t your-internal-registry/fiftyone-app-internal:${FIFTYONE_ENTERPRISE_VERSION} . @@ -63,7 +63,7 @@ After your custom plugins image is built, you can add it to your ```yaml services: teams-plugins: - image: your-internal-registry/fiftyone-app-internal:v2.18.1 + image: your-internal-registry/fiftyone-app-internal:v2.19.0 ``` Please see diff --git a/docs/orchestrators/configuring-databricks-orchestrator.md b/docs/orchestrators/configuring-databricks-orchestrator.md index 9531d319..3086face 100644 --- a/docs/orchestrators/configuring-databricks-orchestrator.md +++ b/docs/orchestrators/configuring-databricks-orchestrator.md @@ -87,7 +87,7 @@ w = WorkspaceClient() DBFS_PATH = "/FileStore/my_project/requirements.txt" PYTHON_DEPENDENCIES = [ - "fiftyone==2.18.1", # use your FiftyOne version here + "fiftyone==2.19.0", # use your FiftyOne version here "ultralytics", "torch", "transformers", diff --git a/helm/docs/upgrading.md b/helm/docs/upgrading.md index f89fe821..873e5e93 100644 --- a/helm/docs/upgrading.md +++ b/helm/docs/upgrading.md @@ -121,9 +121,9 @@ quickstart 0.21.2 ### From FiftyOne Enterprise Version 2.0.0 or Higher -1. [Upgrade to FiftyOne Enterprise version 2.18.1](#upgrading-from-previous-versions) +1. [Upgrade to FiftyOne Enterprise version 2.19.0](#upgrading-from-previous-versions) 1. Voxel51 recommends upgrading all FiftyOne Enterprise SDK users to FiftyOne Enterprise - version 2.18.1 + version 2.19.0 1. Login to the FiftyOne Enterprise UI 1. To obtain the CLI command to install the FiftyOne SDK associated with your FiftyOne Enterprise version, navigate to `Account > Install FiftyOne` @@ -354,7 +354,7 @@ For a full list of settings, please refer to the ### From FiftyOne Enterprise Versions 1.6.0 to 1.7.1 -> **NOTE**: Upgrading to FiftyOne Enterprise v2.18.1 _requires_ a license file. +> **NOTE**: Upgrading to FiftyOne Enterprise v2.19.0 _requires_ a license file. > Please contact your Customer Success Team before upgrading to FiftyOne Enterprise > 2.0 or beyond. > @@ -394,17 +394,17 @@ For a full list of settings, please refer to the fiftyone-license --from-file=license=./your-license-file ``` -1. [Upgrade to FiftyOne Enterprise version 2.18.1](#upgrading-from-previous-versions) -1. Upgrade FiftyOne Enterprise SDK users to FiftyOne Enterprise version 2.18.1 +1. [Upgrade to FiftyOne Enterprise version 2.19.0](#upgrading-from-previous-versions) +1. Upgrade FiftyOne Enterprise SDK users to FiftyOne Enterprise version 2.19.0 1. Login to the FiftyOne Enterprise UI 1. To obtain the CLI command to install the FiftyOne SDK associated with your FiftyOne Enterprise version, navigate to `Account > Install FiftyOne` 1. Upgrade all the datasets - > **NOTE**: Any FiftyOne SDK less than 2.18.1 will lose connectivity after + > **NOTE**: Any FiftyOne SDK less than 2.19.0 will lose connectivity after > this point. - > Upgrading all SDKs to `fiftyone==2.18.1` is recommended before migrating + > Upgrading all SDKs to `fiftyone==2.19.0` is recommended before migrating > your database. ```shell @@ -419,7 +419,7 @@ For a full list of settings, please refer to the ### From FiftyOne Enterprise Versions After 1.1.0 and Before Version 1.6.0 -> **NOTE**: Upgrading to FiftyOne Enterprise v2.18.1 _requires_ +> **NOTE**: Upgrading to FiftyOne Enterprise v2.19.0 _requires_ > your users to log in after the upgrade is complete. > This will interrupt active workflows in the FiftyOne Enterprise Hosted > Web App. You should coordinate this upgrade carefully with your @@ -438,7 +438,7 @@ For a full list of settings, please refer to the --- -> **NOTE**: Upgrading to FiftyOne Enterprise v2.18.1 _requires_ a license file. +> **NOTE**: Upgrading to FiftyOne Enterprise v2.19.0 _requires_ a license file. > Please contact your Customer Success Team before upgrading to FiftyOne Enterprise > 2.0 or beyond. > @@ -478,17 +478,17 @@ For a full list of settings, please refer to the 1. `secret.fiftyone.fiftyoneAuthSecret` (or your deployment's equivalent) 1. This sets the `FIFTYONE_AUTH_SECRET` environment variable in the appropriate service pods -1. [Upgrade to FiftyOne Enterprise version 2.18.1](#upgrading-from-previous-versions) -1. Upgrade FiftyOne Enterprise SDK users to FiftyOne Enterprise version 2.18.1 +1. [Upgrade to FiftyOne Enterprise version 2.19.0](#upgrading-from-previous-versions) +1. Upgrade FiftyOne Enterprise SDK users to FiftyOne Enterprise version 2.19.0 1. Login to the FiftyOne Enterprise UI 1. To obtain the CLI command to install the FiftyOne SDK associated with your FiftyOne Enterprise version, navigate to `Account > Install FiftyOne` 1. Upgrade all the datasets - > **NOTE**: Any FiftyOne SDK less than 2.18.1 will lose connectivity after + > **NOTE**: Any FiftyOne SDK less than 2.19.0 will lose connectivity after > this point. - > Upgrading all SDKs to `fiftyone==2.18.1` is recommended before migrating + > Upgrading all SDKs to `fiftyone==2.19.0` is recommended before migrating > your database. ```shell @@ -520,14 +520,14 @@ For a full list of settings, please refer to the --- -> **NOTE**: Upgrading to FiftyOne Enterprise v2.18.1 _requires_ +> **NOTE**: Upgrading to FiftyOne Enterprise v2.19.0 _requires_ > your users to log in after the upgrade is complete. > This will interrupt active workflows in the FiftyOne Enterprise Hosted Web App. > You should coordinate this upgrade carefully with your end-users. --- -> **NOTE**: Upgrading to FiftyOne Enterprise v2.18.1 _requires_ a license file. +> **NOTE**: Upgrading to FiftyOne Enterprise v2.19.0 _requires_ a license file. > Please contact your Customer Success Team before upgrading to FiftyOne Enterprise > 2.0 or beyond. > @@ -566,10 +566,10 @@ For a full list of settings, please refer to the fiftyone-license --from-file=license=./your-license-file ``` -1. [Upgrade to FiftyOne Enterprise v2.18.1](#upgrading-from-previous-versions) +1. [Upgrade to FiftyOne Enterprise v2.19.0](#upgrading-from-previous-versions) > **NOTE**: At this step, FiftyOne SDK users will lose access to the - > FiftyOne Enterprise Database until they upgrade to `fiftyone==2.18.1` -1. Upgrade your FiftyOne SDKs to version 2.18.1 + > FiftyOne Enterprise Database until they upgrade to `fiftyone==2.19.0` +1. Upgrade your FiftyOne SDKs to version 2.19.0 1. Login to the FiftyOne Enterprise UI 1. To obtain the CLI command to install the FiftyOne SDK associated with your FiftyOne Enterprise version, navigate to diff --git a/helm/fiftyone-teams-app/Chart.yaml b/helm/fiftyone-teams-app/Chart.yaml index abadb4dd..1ab991a6 100644 --- a/helm/fiftyone-teams-app/Chart.yaml +++ b/helm/fiftyone-teams-app/Chart.yaml @@ -6,7 +6,7 @@ description: | FiftyOne Enterprise is the enterprise version of the open source [FiftyOne](https://github.com/voxel51/fiftyone) project. The FiftyOne Enterprise Helm chart is the recommended way to install and configure FiftyOne Enterprise on Kubernetes. type: application -version: 2.18.1 -appVersion: "v2.18.1" +version: 2.19.0 +appVersion: "v2.19.0" icon: https://voxel51.com/images/logo/voxel51-logo-horz-color-600dpi.png kubeVersion: ">=1.31-0" diff --git a/helm/fiftyone-teams-app/README.md b/helm/fiftyone-teams-app/README.md index f85817c8..31c6c8f9 100644 --- a/helm/fiftyone-teams-app/README.md +++ b/helm/fiftyone-teams-app/README.md @@ -15,7 +15,7 @@ # fiftyone-teams-app -![Version: 2.18.1](https://img.shields.io/badge/Version-2.18.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v2.18.1](https://img.shields.io/badge/AppVersion-v2.18.1-informational?style=flat-square) +![Version: 2.19.0](https://img.shields.io/badge/Version-2.19.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v2.19.0](https://img.shields.io/badge/AppVersion-v2.19.0-informational?style=flat-square) FiftyOne Enterprise is the enterprise version of the open source [FiftyOne](https://github.com/voxel51/fiftyone) project. The FiftyOne Enterprise Helm chart is the recommended way to install and configure FiftyOne Enterprise on Kubernetes. @@ -1040,7 +1040,7 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): | teamsAppSettings.env.FIFTYONE_APP_ALLOW_MEDIA_EXPORT | bool | `true` | When `false`, disables media export options | | teamsAppSettings.env.FIFTYONE_APP_ANONYMOUS_ANALYTICS_ENABLED | bool | `true` | Controls whether anonymous analytics are captured for the application. Set to false to opt-out of anonymous analytics. | | teamsAppSettings.env.FIFTYONE_APP_DEPLOYMENT_CHARACTERISTICS | string | `"kubernetes"` | Deployment characteristics for the `teams-app`. `kubernetes`: Indicates the app is running in a Kubernetes environment. `docker`: Indicates the app is running in a Docker environment. `kubernetes,managed`: Indicates the app is running in a managed Kubernetes environment | -| teamsAppSettings.env.FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION | string | `"2.18.1"` | The recommended fiftyone SDK version that will be displayed in the install modal (i.e. `pip install ... fiftyone==0.11.0`). | +| teamsAppSettings.env.FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION | string | `"2.19.0"` | The recommended fiftyone SDK version that will be displayed in the install modal (i.e. `pip install ... fiftyone==0.11.0`). | | teamsAppSettings.env.FIFTYONE_APP_THEME | string | `"dark"` | The default theme configuration. `dark`: Theme will be dark when user visits for the first time. `light`: Theme will be light theme when user visits for the first time. `always-dark`: Sets dark theme on each refresh (overrides user theme changes in the app). `always-light`: Sets light theme on each refresh (overrides user theme changes in the app). | | teamsAppSettings.env.RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED | bool | `false` | Disable duplicate atom/selector key checking that generated false-positive errors. [Reference][recoil-env]. | | teamsAppSettings.fiftyoneApiOverride | string | `""` | Overrides the `FIFTYONE_API_URI` environment variable. When set `FIFTYONE_API_URI` controls the value shown in the API Key Modal providing guidance for connecting to the FiftyOne Enterprise API. `FIFTYONE_API_URI` uses the value from apiSettings.dnsName if it is set, or uses the teamsAppSettings.dnsName | diff --git a/helm/fiftyone-teams-app/values.schema.json b/helm/fiftyone-teams-app/values.schema.json index fbd55d46..0e25880c 100644 --- a/helm/fiftyone-teams-app/values.schema.json +++ b/helm/fiftyone-teams-app/values.schema.json @@ -3440,7 +3440,7 @@ "type": "string" }, "FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION": { - "default": "2.18.1", + "default": "2.19.0", "description": "The recommended fiftyone SDK version that will be displayed in the\ninstall modal (i.e. `pip install ... fiftyone==0.11.0`).", "title": "FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION", "type": "string" diff --git a/helm/fiftyone-teams-app/values.yaml b/helm/fiftyone-teams-app/values.yaml index 404b37d0..890eb603 100644 --- a/helm/fiftyone-teams-app/values.yaml +++ b/helm/fiftyone-teams-app/values.yaml @@ -1400,7 +1400,7 @@ teamsAppSettings: FIFTYONE_APP_ALLOW_MEDIA_EXPORT: true # -- The recommended fiftyone SDK version that will be displayed in the # install modal (i.e. `pip install ... fiftyone==0.11.0`). - FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION: 2.18.1 + FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION: 2.19.0 # -- The default theme configuration. # `dark`: Theme will be dark when user visits for the first time. # `light`: Theme will be light theme when user visits for the first time. diff --git a/helm/values.yaml b/helm/values.yaml index df312cfd..b4795488 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -106,7 +106,7 @@ teamsAppSettings: # env: # # Set FIFTYONE_APP_INSTALL_FIFTYONE_OVERRIDE to override the `Install FiftyOne` # # bash command in the `Settings > Install FiftyOne` modal -# FIFTYONE_APP_INSTALL_FIFTYONE_OVERRIDE: pip install --index-url https://privatepypi.internal.org fiftyone==2.18.1 +# FIFTYONE_APP_INSTALL_FIFTYONE_OVERRIDE: pip install --index-url https://privatepypi.internal.org fiftyone==2.19.0 # ingress: # annotations: diff --git a/skaffold.yaml b/skaffold.yaml index a9d42831..d3d22b36 100644 --- a/skaffold.yaml +++ b/skaffold.yaml @@ -41,7 +41,7 @@ deploy: releases: - name: fiftyone-teams chartPath: helm/fiftyone-teams-app - version: 2.18.1 + version: 2.19.0 createNamespace: true namespace: '{{.FIFTYONE_TEAMS_NAMESPACE | default "fiftyone-teams" }}' overrides: @@ -54,7 +54,7 @@ deploy: # 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.0 # FiftyOne App (fiftyone-app) configurations appSettings: @@ -68,7 +68,7 @@ deploy: # 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.0 # Central Authentication Service (teams-cas) configurations casSettings: @@ -78,7 +78,7 @@ deploy: # 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 # 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` @@ -156,7 +156,7 @@ deploy: # 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.0 # FiftyOne Teams App (teams-app) configurations teamsAppSettings: @@ -175,6 +175,6 @@ deploy: # 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 kubeContext: minikube diff --git a/tests/unit/compose/docker-compose-internal-auth_test.go b/tests/unit/compose/docker-compose-internal-auth_test.go index 62908d41..f96cccd5 100644 --- a/tests/unit/compose/docker-compose-internal-auth_test.go +++ b/tests/unit/compose/docker-compose-internal-auth_test.go @@ -150,42 +150,42 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceImage() { "fiftyone-app", []string{internalAuthComposeFile}, s.dotEnvFiles, - "voxel51/fiftyone-app:v2.18.1", + "voxel51/fiftyone-app:v2.19.0", }, { "defaultTeamsApi", "teams-api", []string{internalAuthComposeFile}, s.dotEnvFiles, - "voxel51/fiftyone-teams-api:v2.18.1", + "voxel51/fiftyone-teams-api:v2.19.0", }, { "defaultTeamsApp", "teams-app", []string{internalAuthComposeFile}, s.dotEnvFiles, - "voxel51/fiftyone-teams-app:v2.18.1", + "voxel51/fiftyone-teams-app:v2.19.0", }, { "defaultTeamsCas", "teams-cas", []string{internalAuthComposeFile}, s.dotEnvFiles, - "voxel51/fiftyone-teams-cas:v2.18.1", + "voxel51/fiftyone-teams-cas:v2.19.0", }, { "dedicatedPluginsTeamsPlugins", "teams-plugins", []string{internalAuthComposeDedicatedPluginsFile}, s.dotEnvFiles, - "voxel51/fiftyone-app-torch:v2.18.1", + "voxel51/fiftyone-app-torch:v2.19.0", }, { "delegatedOperationsTeamsDo", "teams-do", []string{internalAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, - "voxel51/fiftyone-teams-cv-full:v2.18.1", + "voxel51/fiftyone-teams-cv-full:v2.19.0", }, } @@ -282,7 +282,7 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceEnvironment() { "APP_USE_HTTPS=true", "FIFTYONE_API_URI=https://example-api.fiftyone.ai", "FIFTYONE_APP_ALLOW_MEDIA_EXPORT=true", - "FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION=2.18.1", + "FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION=2.19.0", "FIFTYONE_AUTH_SECRET=test-fiftyone-auth-secret", "FIFTYONE_SERVER_ADDRESS=", "FIFTYONE_SERVER_PATH_PREFIX=/api/proxy/fiftyone-teams", @@ -367,7 +367,7 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceEnvironment() { "APP_USE_HTTPS=true", "FIFTYONE_API_URI=https://example-api.fiftyone.ai", "FIFTYONE_APP_ALLOW_MEDIA_EXPORT=true", - "FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION=2.18.1", + "FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION=2.19.0", "FIFTYONE_AUTH_SECRET=test-fiftyone-auth-secret", "FIFTYONE_SERVER_ADDRESS=", "FIFTYONE_SERVER_PATH_PREFIX=/api/proxy/fiftyone-teams", @@ -451,7 +451,7 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceEnvironment() { "APP_USE_HTTPS=true", "FIFTYONE_API_URI=https://example-api.fiftyone.ai", "FIFTYONE_APP_ALLOW_MEDIA_EXPORT=true", - "FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION=2.18.1", + "FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION=2.19.0", "FIFTYONE_AUTH_SECRET=test-fiftyone-auth-secret", "FIFTYONE_SERVER_ADDRESS=", "FIFTYONE_SERVER_PATH_PREFIX=/api/proxy/fiftyone-teams", diff --git a/tests/unit/compose/docker-compose-legacy-auth_test.go b/tests/unit/compose/docker-compose-legacy-auth_test.go index 2e3712ae..1a48c5c4 100644 --- a/tests/unit/compose/docker-compose-legacy-auth_test.go +++ b/tests/unit/compose/docker-compose-legacy-auth_test.go @@ -150,42 +150,42 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceImage() { "fiftyone-app", []string{legacyAuthComposeFile}, s.dotEnvFiles, - "voxel51/fiftyone-app:v2.18.1", + "voxel51/fiftyone-app:v2.19.0", }, { "defaultTeamsApi", "teams-api", []string{legacyAuthComposeFile}, s.dotEnvFiles, - "voxel51/fiftyone-teams-api:v2.18.1", + "voxel51/fiftyone-teams-api:v2.19.0", }, { "defaultTeamsApp", "teams-app", []string{legacyAuthComposeFile}, s.dotEnvFiles, - "voxel51/fiftyone-teams-app:v2.18.1", + "voxel51/fiftyone-teams-app:v2.19.0", }, { "defaultTeamsCas", "teams-cas", []string{legacyAuthComposeFile}, s.dotEnvFiles, - "voxel51/fiftyone-teams-cas:v2.18.1", + "voxel51/fiftyone-teams-cas:v2.19.0", }, { "dedicatedPluginsTeamsPlugins", "teams-plugins", []string{legacyAuthComposeDedicatedPluginsFile}, s.dotEnvFiles, - "voxel51/fiftyone-app-torch:v2.18.1", + "voxel51/fiftyone-app-torch:v2.19.0", }, { "delegatedOperationsTeamsDo", "teams-do", []string{legacyAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, - "voxel51/fiftyone-teams-cv-full:v2.18.1", + "voxel51/fiftyone-teams-cv-full:v2.19.0", }, } @@ -282,7 +282,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceEnvironment() { "APP_USE_HTTPS=true", "FIFTYONE_API_URI=https://example-api.fiftyone.ai", "FIFTYONE_APP_ALLOW_MEDIA_EXPORT=true", - "FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION=2.18.1", + "FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION=2.19.0", "FIFTYONE_AUTH_SECRET=test-fiftyone-auth-secret", "FIFTYONE_SERVER_ADDRESS=", "FIFTYONE_SERVER_PATH_PREFIX=/api/proxy/fiftyone-teams", @@ -364,7 +364,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceEnvironment() { "APP_USE_HTTPS=true", "FIFTYONE_API_URI=https://example-api.fiftyone.ai", "FIFTYONE_APP_ALLOW_MEDIA_EXPORT=true", - "FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION=2.18.1", + "FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION=2.19.0", "FIFTYONE_AUTH_SECRET=test-fiftyone-auth-secret", "FIFTYONE_SERVER_ADDRESS=", "FIFTYONE_SERVER_PATH_PREFIX=/api/proxy/fiftyone-teams", @@ -448,7 +448,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceEnvironment() { "APP_USE_HTTPS=true", "FIFTYONE_API_URI=https://example-api.fiftyone.ai", "FIFTYONE_APP_ALLOW_MEDIA_EXPORT=true", - "FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION=2.18.1", + "FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION=2.19.0", "FIFTYONE_AUTH_SECRET=test-fiftyone-auth-secret", "FIFTYONE_SERVER_ADDRESS=", "FIFTYONE_SERVER_PATH_PREFIX=/api/proxy/fiftyone-teams", @@ -531,7 +531,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceEnvironment() { "APP_USE_HTTPS=true", "FIFTYONE_API_URI=https://example-api.fiftyone.ai", "FIFTYONE_APP_ALLOW_MEDIA_EXPORT=true", - "FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION=2.18.1", + "FIFTYONE_APP_TEAMS_SDK_RECOMMENDED_VERSION=2.19.0", "FIFTYONE_AUTH_SECRET=test-fiftyone-auth-secret", "FIFTYONE_SERVER_ADDRESS=", "FIFTYONE_SERVER_PATH_PREFIX=/api/proxy/fiftyone-teams", From a22d6350ef2c77fcedda3dc8a5f8d7da2ab1b0d1 Mon Sep 17 00:00:00 2001 From: voxelbot Date: Thu, 14 May 2026 16:30:59 +0000 Subject: [PATCH 02/10] ci(fixtures): automated bump for integration test fixtures teams-app:v2.19.0-rc.0 api:v2.19.0rc1 app:v2.19.0rc1 cas:v2.19.0-rc.0 --- .../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 677024ad..eadb40bb 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.0rc1 +FIFTYONE_APP_VERSION=v2.19.0rc1 +FIFTYONE_TEAMS_API_VERSION=v2.19.0rc1 +FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.0 +FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.0 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 1d77f397..40e55115 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.0rc1 +FIFTYONE_APP_VERSION=v2.19.0rc1 +FIFTYONE_TEAMS_API_VERSION=v2.19.0rc1 +FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.0 +FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.0 FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS=1 diff --git a/tests/fixtures/helm/integration_values.yaml b/tests/fixtures/helm/integration_values.yaml index f66afa15..5c4d9889 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.0rc1 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.0rc1 # 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.0 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.0rc1 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.0rc1 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.0rc1 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.0 From ba6407b8e5280efed6adeb763bb1a75e1942e7c8 Mon Sep 17 00:00:00 2001 From: voxelbot Date: Thu, 14 May 2026 20:29:17 +0000 Subject: [PATCH 03/10] ci(fixtures): automated bump for integration test fixtures teams-app:v2.19.0-rc.1 api:v2.19.0rc2 app:v2.19.0rc2 cas:v2.19.0-rc.1 --- .../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 eadb40bb..234c97ff 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.0rc1 -FIFTYONE_APP_VERSION=v2.19.0rc1 -FIFTYONE_TEAMS_API_VERSION=v2.19.0rc1 -FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.0 -FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.0 +VERSION=v2.19.0rc2 +FIFTYONE_APP_VERSION=v2.19.0rc2 +FIFTYONE_TEAMS_API_VERSION=v2.19.0rc2 +FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.1 +FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.1 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 40e55115..0134809b 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.0rc1 -FIFTYONE_APP_VERSION=v2.19.0rc1 -FIFTYONE_TEAMS_API_VERSION=v2.19.0rc1 -FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.0 -FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.0 +VERSION=v2.19.0rc2 +FIFTYONE_APP_VERSION=v2.19.0rc2 +FIFTYONE_TEAMS_API_VERSION=v2.19.0rc2 +FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.1 +FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.1 FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS=1 diff --git a/tests/fixtures/helm/integration_values.yaml b/tests/fixtures/helm/integration_values.yaml index 5c4d9889..a87cc8b6 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.0rc1 + tag: v2.19.0rc2 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.0rc1 + tag: v2.19.0rc2 # 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.0 + tag: v2.19.0-rc.1 delegatedOperatorDeployments: deployments: teamsDoCpuDefault: image: repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.19.0rc1 + tag: v2.19.0rc2 delegatedOperatorTemplates: jobs: teamsDoCpuDefaultK8s: image: repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.19.0rc1 + tag: v2.19.0rc2 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.0rc1 + tag: v2.19.0rc2 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.0 + tag: v2.19.0-rc.1 From 2a688744a306931b952d51e7e4360cda2d056f55 Mon Sep 17 00:00:00 2001 From: voxelbot Date: Fri, 15 May 2026 00:18:04 +0000 Subject: [PATCH 04/10] ci(fixtures): automated bump for integration test fixtures teams-app:v2.19.0-rc.2 api:v2.19.0rc3 app:v2.19.0rc3 cas:v2.19.0-rc.2 --- .../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 234c97ff..1198d1e6 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.0rc2 -FIFTYONE_APP_VERSION=v2.19.0rc2 -FIFTYONE_TEAMS_API_VERSION=v2.19.0rc2 -FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.1 -FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.1 +VERSION=v2.19.0rc3 +FIFTYONE_APP_VERSION=v2.19.0rc3 +FIFTYONE_TEAMS_API_VERSION=v2.19.0rc3 +FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.2 +FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.2 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 0134809b..2690e313 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.0rc2 -FIFTYONE_APP_VERSION=v2.19.0rc2 -FIFTYONE_TEAMS_API_VERSION=v2.19.0rc2 -FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.1 -FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.1 +VERSION=v2.19.0rc3 +FIFTYONE_APP_VERSION=v2.19.0rc3 +FIFTYONE_TEAMS_API_VERSION=v2.19.0rc3 +FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.2 +FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.2 FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS=1 diff --git a/tests/fixtures/helm/integration_values.yaml b/tests/fixtures/helm/integration_values.yaml index a87cc8b6..4843c2a8 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.0rc2 + tag: v2.19.0rc3 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.0rc2 + tag: v2.19.0rc3 # 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.1 + tag: v2.19.0-rc.2 delegatedOperatorDeployments: deployments: teamsDoCpuDefault: image: repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.19.0rc2 + tag: v2.19.0rc3 delegatedOperatorTemplates: jobs: teamsDoCpuDefaultK8s: image: repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.19.0rc2 + tag: v2.19.0rc3 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.0rc2 + tag: v2.19.0rc3 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.1 + tag: v2.19.0-rc.2 From 6fa7142246bee6bb44fb0861c21c7e0715bdaee7 Mon Sep 17 00:00:00 2001 From: voxelbot Date: Mon, 18 May 2026 21:03:06 +0000 Subject: [PATCH 05/10] ci(fixtures): automated bump for integration test fixtures teams-app:v2.19.0-rc.3 api:v2.19.0rc4 app:v2.19.0rc4 cas:v2.19.0-rc.3 --- .../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 1198d1e6..04b404df 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.0rc3 -FIFTYONE_APP_VERSION=v2.19.0rc3 -FIFTYONE_TEAMS_API_VERSION=v2.19.0rc3 -FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.2 -FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.2 +VERSION=v2.19.0rc4 +FIFTYONE_APP_VERSION=v2.19.0rc4 +FIFTYONE_TEAMS_API_VERSION=v2.19.0rc4 +FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.3 +FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.3 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 2690e313..7f9eb670 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.0rc3 -FIFTYONE_APP_VERSION=v2.19.0rc3 -FIFTYONE_TEAMS_API_VERSION=v2.19.0rc3 -FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.2 -FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.2 +VERSION=v2.19.0rc4 +FIFTYONE_APP_VERSION=v2.19.0rc4 +FIFTYONE_TEAMS_API_VERSION=v2.19.0rc4 +FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.3 +FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.3 FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS=1 diff --git a/tests/fixtures/helm/integration_values.yaml b/tests/fixtures/helm/integration_values.yaml index 4843c2a8..55c29805 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.0rc3 + tag: v2.19.0rc4 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.0rc3 + tag: v2.19.0rc4 # 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.2 + tag: v2.19.0-rc.3 delegatedOperatorDeployments: deployments: teamsDoCpuDefault: image: repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.19.0rc3 + tag: v2.19.0rc4 delegatedOperatorTemplates: jobs: teamsDoCpuDefaultK8s: image: repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.19.0rc3 + tag: v2.19.0rc4 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.0rc3 + tag: v2.19.0rc4 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.2 + tag: v2.19.0-rc.3 From b6b9576cf06aa94c77c00fd492eb480b313e7d0e Mon Sep 17 00:00:00 2001 From: voxelbot Date: Tue, 19 May 2026 18:50:42 +0000 Subject: [PATCH 06/10] ci(fixtures): automated bump for integration test fixtures teams-app:v2.19.0-rc.4 api:v2.19.0rc5 app:v2.19.0rc5 cas:v2.19.0-rc.4 --- .../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 04b404df..48e46857 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.0rc4 -FIFTYONE_APP_VERSION=v2.19.0rc4 -FIFTYONE_TEAMS_API_VERSION=v2.19.0rc4 -FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.3 -FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.3 +VERSION=v2.19.0rc5 +FIFTYONE_APP_VERSION=v2.19.0rc5 +FIFTYONE_TEAMS_API_VERSION=v2.19.0rc5 +FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.4 +FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.4 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 7f9eb670..f1728dbd 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.0rc4 -FIFTYONE_APP_VERSION=v2.19.0rc4 -FIFTYONE_TEAMS_API_VERSION=v2.19.0rc4 -FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.3 -FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.3 +VERSION=v2.19.0rc5 +FIFTYONE_APP_VERSION=v2.19.0rc5 +FIFTYONE_TEAMS_API_VERSION=v2.19.0rc5 +FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.4 +FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.4 FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS=1 diff --git a/tests/fixtures/helm/integration_values.yaml b/tests/fixtures/helm/integration_values.yaml index 55c29805..cb75b126 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.0rc4 + tag: v2.19.0rc5 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.0rc4 + tag: v2.19.0rc5 # 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.3 + tag: v2.19.0-rc.4 delegatedOperatorDeployments: deployments: teamsDoCpuDefault: image: repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.19.0rc4 + tag: v2.19.0rc5 delegatedOperatorTemplates: jobs: teamsDoCpuDefaultK8s: image: repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.19.0rc4 + tag: v2.19.0rc5 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.0rc4 + tag: v2.19.0rc5 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.3 + tag: v2.19.0-rc.4 From 195cff9f18f27d8429f3d19ae7866c76877e353b Mon Sep 17 00:00:00 2001 From: Kevin DiMichel <56850465+kevin-dimichel@users.noreply.github.com> Date: Wed, 20 May 2026 11:57:02 -0600 Subject: [PATCH 07/10] chore: fix helm.fiftyone.ai table [AS-1454] (#568) chore: fix helm.fiftyone.ai table --- helm/fiftyone-teams-app/README.md | 2 ++ helm/fiftyone-teams-app/README.md.gotmpl | 2 ++ 2 files changed, 4 insertions(+) diff --git a/helm/fiftyone-teams-app/README.md b/helm/fiftyone-teams-app/README.md index 31c6c8f9..8b066e31 100644 --- a/helm/fiftyone-teams-app/README.md +++ b/helm/fiftyone-teams-app/README.md @@ -712,6 +712,7 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): ## Values + | Key | Type | Default | Description | |-----|------|---------|-------------| | apiSettings.affinity | object | `{}` | Affinity and anti-affinity for `teams-api`. [Reference][affinity]. | @@ -1084,6 +1085,7 @@ 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]. | + diff --git a/helm/fiftyone-teams-app/README.md.gotmpl b/helm/fiftyone-teams-app/README.md.gotmpl index ae699448..cbf38123 100644 --- a/helm/fiftyone-teams-app/README.md.gotmpl +++ b/helm/fiftyone-teams-app/README.md.gotmpl @@ -718,7 +718,9 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): ## Values + {{ template "chart.valuesTable" . }} + From 3feb5555032040a8d23199c9e48fe91b54e628be Mon Sep 17 00:00:00 2001 From: Alan Smith <209585+mo-getter@users.noreply.github.com> Date: Wed, 27 May 2026 22:12:45 -0400 Subject: [PATCH 08/10] feat(telemetry): integrate the new telemetry sidecar (#566) Enables the new observability features released in FiftyOne Enterprise 2.19.0, including enhanced process metrics, real-time logs, and more, viewable directly within the UI. Changes: * Helm * Adds a sidecar container to the following pods: * fiftyone-app * teams-api * plugins * do deployments * do jobs (as a native sidecar) * Adds a role and rolebinding for telemetry * Adds get pods/log permission to api role * Adds an optional redis deployment and service, with a further optional chart-managed pvc, or a further optional existing PVC by name * Compose * Drops support for `FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS` config option, replacing it with `COMPOSE_PROFILES="do-3"` * Adds sidecar containers to same services as Helm * Adds a tmpfs volume for the telemetry socket * Scripts: Adds support for the new telemetry-sidecar image --------- Signed-off-by: Alan Smith <209585+mo-getter@users.noreply.github.com> Signed-off-by: Kacey Co-authored-by: kacey Co-authored-by: Kevin DiMichel <56850465+kevin-dimichel@users.noreply.github.com> --- docker/README.md | 12 + docker/common-services.yaml | 148 ++++++- .../docs/configuring-delegated-operators.md | 13 + docker/docs/configuring-gpu-workloads.md | 20 +- docker/docs/configuring-telemetry.md | 261 +++++++++++ docker/docs/upgrading.md | 106 +++++ .../compose.dedicated-plugins.yaml | 39 ++ .../compose.delegated-operators.gpu.yaml | 102 +++++ .../compose.delegated-operators.yaml | 156 +++++++ docker/internal-auth/compose.plugins.yaml | 28 ++ docker/internal-auth/compose.yaml | 30 ++ docker/internal-auth/env.template | 42 +- .../compose.dedicated-plugins.yaml | 39 ++ .../compose.delegated-operators.gpu.yaml | 102 +++++ .../compose.delegated-operators.yaml | 158 +++++++ docker/legacy-auth/compose.plugins.yaml | 28 ++ docker/legacy-auth/compose.yaml | 32 ++ docker/legacy-auth/env.template | 45 +- .../configuring-kubernetes-orchestrator.md | 175 ++++++++ helm/docs/upgrading.md | 72 +++ helm/fiftyone-teams-app/README.md | 30 +- helm/fiftyone-teams-app/templates/NOTES.txt | 9 +- .../templates/_do_targets.tpl | 6 + .../fiftyone-teams-app/templates/_helpers.tpl | 9 + .../templates/_telemetry.tpl | 206 +++++++++ .../templates/api-deployment.yaml | 6 + .../templates/api-role.yaml | 3 + .../templates/app-deployment.yaml | 6 + ...elegated-operator-instance-deployment.yaml | 29 +- .../delegated-operator-job-configmap.yaml | 30 +- .../templates/plugins-deployment.yaml | 6 + .../templates/telemetry-redis-deployment.yaml | 60 +++ .../templates/telemetry-redis-pvc.yaml | 20 + .../templates/telemetry-redis-service.yaml | 15 + .../templates/telemetry-role.yaml | 16 + .../templates/telemetry-rolebinding.yaml | 15 + helm/fiftyone-teams-app/values.schema.json | 401 ++++++++++++++++- helm/fiftyone-teams-app/values.yaml | 124 +++++- tests/fixtures/docker/.env | 2 - .../docker/compose.override.mongodb.yaml | 4 + .../docker/compose.override.mongodb_do.yaml | 2 + .../compose.override.mongodb_plugins.yaml | 2 + .../docker/integration_internal_auth.env | 12 +- .../docker/integration_legacy_auth.env | 12 +- tests/fixtures/helm/integration_values.yaml | 20 +- .../docker-compose-internal-auth_test.go | 16 +- .../docker-compose-legacy-auth_test.go | 14 +- tests/unit/compose/common_test.go | 8 + .../docker-compose-internal-auth_test.go | 148 ++++++- .../docker-compose-legacy-auth_test.go | 151 ++++++- tests/unit/helm/api-deployment_test.go | 81 ++-- tests/unit/helm/api-role_test.go | 45 +- tests/unit/helm/app-deployment_test.go | 74 ++-- tests/unit/helm/common_test.go | 4 +- ...gated-operator-instance-deployment_test.go | 216 ++++++--- .../delegated-operator-job-configmap_test.go | 116 ++++- tests/unit/helm/plugins-deployment_test.go | 116 ++--- tests/unit/helm/teams-app-deployment_test.go | 66 +-- tests/unit/helm/telemetry-redis_test.go | 412 ++++++++++++++++++ tests/unit/helm/telemetry-rolebinding_test.go | 188 ++++++++ tests/unit/helm/telemetry-sidecar_test.go | 197 +++++++++ ...-cpu-default-override-template-values.yaml | 3 + .../expected-cpu-default.yaml | 5 + ...d-override-example-cascading-template.yaml | 3 + .../expected-override-example-template.yaml | 3 + tests/unit/helm/yaml_helpers.go | 31 ++ utils/bump-fixtures-helm.sh | 4 + utils/validate-docker-pulls.sh | 4 + 68 files changed, 4224 insertions(+), 334 deletions(-) create mode 100644 docker/docs/configuring-telemetry.md create mode 100644 docker/internal-auth/compose.delegated-operators.gpu.yaml create mode 100644 docker/legacy-auth/compose.delegated-operators.gpu.yaml create mode 100644 helm/fiftyone-teams-app/templates/_telemetry.tpl create mode 100644 helm/fiftyone-teams-app/templates/telemetry-redis-deployment.yaml create mode 100644 helm/fiftyone-teams-app/templates/telemetry-redis-pvc.yaml create mode 100644 helm/fiftyone-teams-app/templates/telemetry-redis-service.yaml create mode 100644 helm/fiftyone-teams-app/templates/telemetry-role.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/telemetry-sidecar_test.go create mode 100644 tests/unit/helm/yaml_helpers.go diff --git a/docker/README.md b/docker/README.md index 7f231589..605ecdda 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) @@ -656,6 +657,17 @@ and [adding secrets](https://docs.voxel51.com/enterprise/secrets.html#adding-secrets) for questions regarding storage and encryption. +### Telemetry + +FiftyOne Enterprise bundles a telemetry sidecar and 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. + +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/common-services.yaml b/docker/common-services.yaml index bc71259a..c95e93c9 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.19.0 + # 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,144 @@ 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 + + telemetry-redis-common: + 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 + deploy: + resources: + limits: + cpus: "0.25" + memory: 512M + reservations: + cpus: "0.10" + memory: 256M + 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-common: + image: voxel51/telemetry-sidecar:v2.19.0 + pid: "service:fiftyone-app" + # Matches the workload image's USER 1000:1000 so the kernel's + # ptrace_may_access() check on /proc//fd/1 reads passes via the + # same-UID fast path to allow for log tailing + user: "1000:1000" + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + 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} + deploy: + resources: + limits: + cpus: "0.10" + memory: 512M + reservations: + cpus: "0.10" + memory: 512M + restart: always + + teams-api-telemetry-common: + image: voxel51/telemetry-sidecar:v2.19.0 + pid: "service:teams-api" + user: "1000:1000" + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + 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} + deploy: + resources: + limits: + cpus: "0.10" + memory: 512M + reservations: + cpus: "0.10" + memory: 512M + restart: always + + teams-plugins-telemetry-common: + image: voxel51/telemetry-sidecar:v2.19.0 + pid: "service:teams-plugins" + user: "1000:1000" + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + 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} + deploy: + resources: + limits: + cpus: "0.10" + memory: 512M + reservations: + cpus: "0.10" + memory: 512M + restart: always + + teams-do-telemetry-common: + image: voxel51/telemetry-sidecar:v2.19.0 + pid: "service:teams-do" + user: "1000:1000" + cap_drop: + - ALL + cap_add: + - SYS_PTRACE # allows for additional profiling metrics (sampled stacks via py-spy) + security_opt: + - no-new-privileges:true + 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} + deploy: + resources: + limits: + cpus: "0.10" + memory: 512M + reservations: + cpus: "0.10" + memory: 512M + volumes: + - telemetry-socket:/tmp/telemetry + restart: always diff --git a/docker/docs/configuring-delegated-operators.md b/docker/docs/configuring-delegated-operators.md index 6d74625a..51f2c90a 100644 --- a/docker/docs/configuring-delegated-operators.md +++ b/docker/docs/configuring-delegated-operators.md @@ -32,6 +32,19 @@ in conjunction with one of the three plugin configurations. up -d ``` +> [!IMPORTANT] +> The `compose.delegated-operators.yaml` overlay defines three worker +> slots — `teams-do`, `teams-do-2`, `teams-do-3` — each paired with a +> telemetry sidecar. Slot 1 is always on (the default renders one +> observed worker); slots 2 and 3 are gated behind cumulative Compose +> profiles (`do-2`, `do-3`). Set `COMPOSE_PROFILES=do-N` to add slots +> up to N (cap of 3). This replaces the deprecated +> `FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS` environment variable. +> If you used on the <2.19 default of 3 workers, set `COMPOSE_PROFILES=do-3`. +> See +> [Scaling teams-do with telemetry](./configuring-telemetry.md#scaling-teams-do-with-telemetry) +> for the slot/profile reference and the rationale. + Optionally, delegated operation run logs may be uploaded to a network-mounted file system or cloud storage path. Logs are uploaded in the format diff --git a/docker/docs/configuring-gpu-workloads.md b/docker/docs/configuring-gpu-workloads.md index 1e4b12f5..324a88b0 100644 --- a/docker/docs/configuring-gpu-workloads.md +++ b/docker/docs/configuring-gpu-workloads.md @@ -51,11 +51,19 @@ We will configure the [delegated operators](./configuring-delegated-operators.md) with GPUs. -In your `compose.delegated-operators.yaml`, under `.services.teams-do`, -add the `deploy.resource.reservation.devices` configuration. - -The below will deploy a CPU-based delegated operator (`teams-do`) as well -as a GPU-based delegated operator (`teams-do-with-gpu`): +For most deployments, use the shipped GPU overlay +`compose.delegated-operators.gpu.yaml`, which includes the GPU +worker and its paired telemetry sidecar. Activate it alongside the CPU +worker profile (e.g. `COMPOSE_PROFILES=do-1,gpu`); see +[Configuring Telemetry](./configuring-telemetry.md#scaling-teams-do-with-telemetry) +for the full profile reference. + +The snippet below contains custom inline example of +how to add a GPU-based delegated operator (`teams-do-with-gpu`) +alongside the standard `teams-do`. Pair it with telemetry by +adding a matching `teams-do-with-gpu-telemetry` sidecar. See +`docker//compose.delegated-operators.gpu.yaml` for an +example. ```yaml services: @@ -81,7 +89,7 @@ services: volumes: - plugins-vol:/opt/plugins:ro deploy: - replicas: ${FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS:-3} + replicas: 1 resources: reservations: devices: diff --git a/docker/docs/configuring-telemetry.md b/docker/docs/configuring-telemetry.md new file mode 100644 index 00000000..50cb49ac --- /dev/null +++ b/docker/docs/configuring-telemetry.md @@ -0,0 +1,261 @@ + + +
+

+ +Voxel51 Logo   +Voxel51 FiftyOne + +

+
+ + +--- + +# Configuring FiftyOne Enterprise Telemetry + +Telemetry adds a lightweight per-service metrics collector (sidecar +pattern) and a Redis backend. +In the app, the Settings → Metrics page in displays live CPU / memory / +thread / file-descriptor samples and tailed stdout logs for each +observed service. + +**Telemetry is enabled by default.** +The base compose files contain the `telemetry-redis` service and +per-workload sidecars. + +## Default deployment + +Running + +```shell +docker compose -f compose.yaml up -d +``` + +renders `fiftyone-app`, `teams-api`, `teams-app`, `teams-cas`, +`telemetry-redis`, `fiftyone-app-telemetry`, and `teams-api-telemetry`. + +Optional overlays provide their own bundled sidecar. +The `compose.yaml`, `compose.plugins.yaml`, and +`compose.dedicated-plugins.yaml` are mutually exclusive base files — +pick one, then layer the `compose.delegated-operators.yaml` overlay on top. + +The delegated-operators overlay defines three worker slots + +1. `teams-do` (always on) +1. `teams-do-2` + 1. Enabled using the `do-2` and `do-3` Compose profiles +1. `teams-do-3` + 1. Enabled using the `do-3` Compose profile + +The default (no profile) runs one delegated operator worker. +Set `COMPOSE_PROFILES=do-` to add slots up to ``. +`do-` includes previous slots. +For example `do-3` runs three workers. + +To run the dedicated-plugins base with two delegated-operator workers either: + +1. Set `COMPOSE_PROFILES=do-2` in your `.env` +1. Set the environment variable while calling compose up + +This renders the services + +- FiftyOne Enterprise + - `fiftyone-app`, + - `teams-api` + - `teams-app` + - `teams-cas` + - `teams-plugins`, +- Telemetry + - `telemetry-redis` + - `fiftyone-app-telemetry` + - `teams-api-telemetry`, + - `teams-plugins-telemetry` + - `teams-do-telemetry` + - `teams-do-2-telemetry` +- Delegated Operator + - `teams-do` + - `teams-do-2` + +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 \ + up -d +``` + +## What's included + +- Services + - `telemetry-redis` + - Redis 7 container that holds metric streams and log entries. + - Data is capped by the `allkeys-lru` maxmemory policy so disk usage + stays bounded. + - `teams-do-gpu` + - only with `compose.delegated-operators.gpu.yaml` and `--profile gpu` + - GPU-enabled delegated-operator worker registered as a distinct + orchestrator (`-n teams-do-gpu`) plus its paired sidecar. +- Service Side Cars + - `fiftyone-app-telemetry` and `teams-api-telemetry` + - Each joins the target's PID namespace via `pid: "service:"` + so it can read `/proc//fd/1` and use psutil to sample CPU, + memory, file descriptors, and thread counts + - For delegated operations run with the `SYS_PTRACE` capability so + py-spy can attach to the target + - `teams-plugins-telemetry` + - Only with `compose.dedicated-plugins.yaml` + - For the dedicated `teams-plugins` service + - `teams-do-telemetry` + - Only with `compose.delegated-operators.yaml` + - In `EXECUTOR_SIDECAR=true` mode that watches the executor + for per-operation child processes and records per-operation metrics + back to the `delegated_ops` MongoDB document + - Additional workers (`teams-do-2`, `teams-do-3`) + - Opt-in via Compose profiles and each + get their own paired sidecar + - See + [Scaling teams-do with telemetry](#scaling-teams-do-with-telemetry) + - `teams-do-gpu-telemetry` + - Only with `compose.delegated-operators.gpu.yaml` and `--profile gpu` + - The sidecar reads GPU metrics via NVML and requires its own GPU + reservation. +- Environment Variables + - `FIFTYONE_TELEMETRY_REDIS_URL` environment variable is set on these services + so the telemetry blueprint and server-sent events endpoints can read from Redis + - `fiftyone-app` + - `teams-api` + - `teams-app` + - `teams-plugins` + - (when the DO overlay is used) `teams-do` + +## Opting out + +> [!IMPORTANT] +> Disabling the telemetry sidecar leaves the FiftyOne UI's +> delegated-operator log viewer empty. +> The log viewer depends on the sidecar to capture per-operation logs. + +To run without telemetry + +1. Add a `compose.override.yaml` that scales the + telemetry services to zero replicas: + +### Scaling teams-do with telemetry + +Docker Compose's `pid: "service:"` only joins a single replica's PID namespace. +Thus single `teams-do` service scaled to `` replicas +would leave `` of them invisible to the sidecar. +Instead, `compose.delegated-operators.yaml` contains three worker +slots as its own Compose service, paired service sidecar, and +its own executor-socket volume: + +| Slot | Service | Sidecar | Activation | +| ---- | ------------- | ----------------------- | ------------------------ | +| 1 | `teams-do` | `teams-do-telemetry` | always on (no profile) | +| 2 | `teams-do-2` | `teams-do-2-telemetry` | profile `do-2` or `do-3` | +| 3 | `teams-do-3` | `teams-do-3-telemetry` | profile `do-3` | + +The default (no profile) runs one delegated operator worker. To add more, +activate the matching Compose profile — `do-` runs `` workers because +higher numbers include every prior slot: + +```shell +# 1 worker (default): +docker compose -f compose.yaml \ + -f compose.delegated-operators.yaml up -d + +# 2 workers: +COMPOSE_PROFILES=do-2 docker compose -f compose.yaml \ + -f compose.delegated-operators.yaml up -d + +# 3 workers: +COMPOSE_PROFILES=do-3 docker compose -f compose.yaml \ + -f compose.delegated-operators.yaml up -d +``` + +Set `COMPOSE_PROFILES` in your `.env` to persist the choice, or pass +`--profile do-` on the command line. Each worker registers under its +own orchestrator name (slot 2 as `teams-do-2`, slot 3 as `teams-do-3`) +so they surface distinctly in Settings → Metrics. + +> [!IMPORTANT] +> v2.19 deprecates the `FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS` +> environment variable. +> Prior to v2.19, the `teams-do` replica count defaulted to `3`. +> To retain the prior replica count behavior, +> set `COMPOSE_PROFILES=do-3` in your `.env` file. +> [!NOTE] +> The cap of 3 is intentional as the value must not exceed your +> license's max concurrent delegated operators. +> For more than 3 workers, use the slot-3 blocks in +> `compose.delegated-operators.yaml` as templates. +> Duplicate them as `teams-do-4` / `teams-do-4-telemetry`, +> bumping the service name, `pid: "service:teams-do-N"`, `POD_NAME`, +> `-n teams-do-N`, and `telemetry-socket-N` volume on each copy. + +### 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, config +change) the namespace reference goes stale and the sidecar stays in +`Exited (137)` until manually recreated. + +The compose files set `depends_on..restart: true` so the +sidecar is recreated in lockstep with the workload. +This requires Docker Compose v2.17 or newer. +If the workload crash-loops, the sidecar follows it. + +## 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_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_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 | + +## Resource limits + +Telemetry containers ship with conservative CPU and memory limits sized so the +sidecars do not starve the workloads they observe. +The values are declared under each service's `deploy.resources` block +in the Cpmpose files. +Compose v2 honors `cpus` and `memory` limits and reservations outside swarm +mode. + +| Service | CPU limit | Memory limit | Notes | +| ----------------------------- | --------- | ------------ | ----------------------------------- | +| `telemetry-redis` | `0.25` | `512M` | Reserves `0.10` CPU / `256M` memory | +| `*-telemetry` (any sidecar) | `0.10` | `512M` | Reserved == limit | + +To tune these for your hardware, override `deploy.resources` in a +`compose.override.yaml` (the override merges with the base entry). + +## Verify + +```shell +docker compose exec telemetry-redis redis-cli HGETALL active_targets +docker compose exec telemetry-redis redis-cli XLEN metrics: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/docs/upgrading.md b/docker/docs/upgrading.md index 61cf8037..39cb4e5e 100644 --- a/docker/docs/upgrading.md +++ b/docker/docs/upgrading.md @@ -19,6 +19,10 @@ - [Upgrading From Previous Versions](#upgrading-from-previous-versions) - [A Note On Database Migrations](#a-note-on-database-migrations) - [From FiftyOne Enterprise Version 2.0.0 and Later](#from-fiftyone-enterprise-version-200-and-later) + - [FiftyOne Enterprise v2.19+ Telemetry Sidecars](#fiftyone-enterprise-v219-telemetry-sidecars) + - [Host Requirements](#host-requirements) + - [Opting out of Telemetry](#opting-out-of-telemetry) + - [Scaling delegated-operator workers](#scaling-delegated-operator-workers) - [FiftyOne Enterprise v2.16+ Additional API Routes](#fiftyone-enterprise-v216-additional-api-routes) - [FiftyOne Enterprise v2.15+ Additional API Routes](#fiftyone-enterprise-v215-additional-api-routes) - [FiftyOne Enterprise v2.7+ Delegated Operator Changes](#fiftyone-enterprise-v27-delegated-operator-changes) @@ -98,6 +102,108 @@ quickstart 0.21.2 fiftyone migrate --info ``` +#### FiftyOne Enterprise v2.19+ Telemetry Sidecars + +FiftyOne Enterprise v2.19.0 adds observability features viewable by +admins directly in the FiftyOne UI. +These are powered by a `telemetry-sidecar` service paired with each +`fiftyone-app`, `teams-api`, `teams-plugins`, and `teams-do*` service, +plus a `telemetry-redis` service that buffers the streamed metrics and +logs. + +**Resource impact:** +Each sidecar reserves `0.10` CPUs and `512M` memory +(reservation == limit). +A default deploy adds four sidecars +(`fiftyone-app` + `teams-api` + `teams-plugins` + one `teams-do`), so +expect roughly **+0.4 CPU** and **+2 GiB memory** in additional +resource usage, plus the bundled `telemetry-redis` service (`0.10` CPU +/ `256M` memory reservation, `0.25` / `512M` limits) and its +`telemetry-redis-data` named volume. + +##### Host Requirements + +1. **Docker Compose v2.17+** for `depends_on..restart: true` + semantics. + Older versions will see stale PID namespaces after a target + container is recreated. +1. **Delegated-operator workers are scaled via [Compose profiles](https://docs.docker.com/compose/how-tos/profiles/)** + (`do-2`, `do-3`) rather than the deprecated + `FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS` env var, because + Compose's `pid: "service:"` only joins a single replica. + Slot 1 (`teams-do`) is always on, and slots 2-3 each get their own + paired sidecar. See + [Scaling delegated-operator workers](#scaling-delegated-operator-workers) + below. +1. **`teams-do` requires the **`SYS_PTRACE`** capability to allow the + telemetry agent to observe the target process. + +**External Redis:** +To use an existing Redis instance instead of the bundled one by setting +`FIFTYONE_TELEMETRY_REDIS_URL` in your `.env` to a fully-qualified URL +(e.g. `redis://my-managed-redis.example.com:6379`) and scaling +`telemetry-redis` to `replicas: 0` as below. + +##### Opting out of Telemetry + +Telemetry is enabled by default. +To disable it, add a `compose.override.yaml` that scales the +`telemetry-redis` and `*-telemetry` services to `replicas: 0`. +See +[`configuring-telemetry.md`](configuring-telemetry.md#opting-out) +for the full override snippet. + +> [!IMPORTANT] +> The sidecar powers the FiftyOne UI's delegated-operator log viewer. +> Disabling telemetry will leave that log viewer empty. + +##### Scaling delegated-operator workers + +> [!WARNING] +> **Breaking change.** +> `FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS` is deprecated +> in docker compose deployments — setting it has no effect. +> The default rendered worker count drops from **3** (pre-2.19.0) to +> **1** when you layer `compose.delegated-operators.yaml`. + +`compose.delegated-operators.yaml` now declares three worker slots, +each paired with its own telemetry sidecar: + +| Slot | Service | Activation | +| ---- | ------------- | ---------------------- | +| 1 | `teams-do` | always on (no profile) | +| 2 | `teams-do-2` | profile `do-2`, `do-3` | +| 3 | `teams-do-3` | profile `do-3` | + +Set `COMPOSE_PROFILES=do-` to add slots 2-3; profiles are nested so +`do-3` includes slot 2. + +**To preserve the previous default of three workers**, set the +following in your `.env`: + +```shell +COMPOSE_PROFILES=do-3 +``` + +If +you previously set `FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS`, +remove it from your `.env` and pick the matching `do-` profile. +For more than three, the slot-2/3 service blocks can be duplicated +as `teams-do-4` / etc. +(bump the service name, `pid`, `POD_NAME`, `-n`, and socket-volume +name on each copy) — see the per-slot inline comments in +`compose.delegated-operators.yaml`. + +Each worker now registers under its own orchestrator name (slot 2 as +`teams-do-2`, slot 3 as `teams-do-3`) so they surface separately in +Settings → Metrics. Resource impact scales linearly with the worker +count: each additional slot adds ~0.2 CPU and ~1 GiB of memory +(worker reservation + paired sidecar). + +See +[`docker/docs/configuring-telemetry.md`](configuring-telemetry.md#scaling-teams-do-with-telemetry) +for the full slot/profile reference. + #### FiftyOne Enterprise v2.16+ Additional API Routes FiftyOne Enterprise v2.16.0 adds the `/cloud_credentials` endpoint to the `teams-api`. diff --git a/docker/internal-auth/compose.dedicated-plugins.yaml b/docker/internal-auth/compose.dedicated-plugins.yaml index 1cf90e79..503ff802 100644 --- a/docker/internal-auth/compose.dedicated-plugins.yaml +++ b/docker/internal-auth/compose.dedicated-plugins.yaml @@ -39,5 +39,44 @@ services: volumes: - plugins-vol:/opt/plugins:ro + telemetry-redis: + extends: + file: ../common-services.yaml + service: telemetry-redis-common + + fiftyone-app-telemetry: + extends: + file: ../common-services.yaml + service: fiftyone-app-telemetry-common + depends_on: + fiftyone-app: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + + teams-api-telemetry: + extends: + file: ../common-services.yaml + service: teams-api-telemetry-common + depends_on: + teams-api: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + + teams-plugins-telemetry: + extends: + file: ../common-services.yaml + service: teams-plugins-telemetry-common + depends_on: + teams-plugins: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + 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 new file mode 100644 index 00000000..d40f4ccc --- /dev/null +++ b/docker/internal-auth/compose.delegated-operators.gpu.yaml @@ -0,0 +1,102 @@ +--- +# 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 \ +# 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: + teams-do-gpu: + image: voxel51/fiftyone-teams-cv-full:v2.19.0 + 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 + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + NVIDIA_VISIBLE_DEVICES: ${NVIDIA_VISIBLE_DEVICES:-all} + NVIDIA_DRIVER_CAPABILITIES: ${NVIDIA_DRIVER_CAPABILITIES:-compute,utility} + TELEMETRY_SOCKET: /tmp/telemetry/agent.sock + 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: voxel51/telemetry-sidecar:v2.19.0 + pid: "service:teams-do-gpu" + profiles: ["gpu"] + user: "1000:1000" + cap_add: + - SYS_PTRACE # required for profiling metrics (sampled stacks via py-spy) + deploy: + resources: + limits: + cpus: "0.10" + memory: 512M + reservations: + cpus: "0.10" + memory: 512M + 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 + +# tmpfs-backed named volume with uid/gid set at mount time so the +# non-root sidecar can bind agent.sock. +volumes: + plugins-vol: + telemetry-socket-gpu: + driver_opts: + type: tmpfs + device: tmpfs + o: "uid=1000,gid=1000,mode=0755" diff --git a/docker/internal-auth/compose.delegated-operators.yaml b/docker/internal-auth/compose.delegated-operators.yaml index 5337711f..238d8bda 100644 --- a/docker/internal-auth/compose.delegated-operators.yaml +++ b/docker/internal-auth/compose.delegated-operators.yaml @@ -1,4 +1,6 @@ --- +# See docker/docs/configuring-telemetry.md for details on telemetry settings. +# # For Proxy Server instructions please see # https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies services: @@ -7,5 +9,159 @@ services: file: ../common-services.yaml service: teams-do-common + teams-do-telemetry: + extends: + file: ../common-services.yaml + service: teams-do-telemetry-common + depends_on: + teams-do: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + + # Slot 2 — included by the `do-2` and `do-3` profiles. Defined inline + # (rather than extending teams-do-common) so it can own its own + # executor-socket named volume; named volumes are shared by their + # volume name, so reusing `telemetry-socket` across slots would + # clobber the socket. `-n teams-do-2` registers this worker as a + # distinct orchestrator name so it surfaces separately in the + # Settings → Metrics UI. + teams-do-2: + image: voxel51/fiftyone-teams-cv-full:v2.19.0 + profiles: ["do-2", "do-3"] + deploy: + replicas: 1 + command: > + /bin/sh -c "fiftyone delegated launch -t remote -m -n teams-do-2" + 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 + 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-2:/tmp/telemetry + + teams-do-2-telemetry: + image: voxel51/telemetry-sidecar:v2.19.0 + profiles: ["do-2", "do-3"] + pid: "service:teams-do-2" + user: "1000:1000" + cap_drop: + - ALL + cap_add: + - SYS_PTRACE # required for profiling metrics (sampled stacks via py-spy) + security_opt: + - no-new-privileges:true + environment: + POD_NAME: teams-do-2 + 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} + deploy: + resources: + limits: + cpus: "0.10" + memory: 512M + reservations: + cpus: "0.10" + memory: 512M + volumes: + - telemetry-socket-2:/tmp/telemetry + depends_on: + teams-do-2: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + + # Slot 3 — included by the `do-3` profile. Mirror of slot 2 with its + # own socket volume and orchestrator name. + teams-do-3: + image: voxel51/fiftyone-teams-cv-full:v2.19.0 + profiles: ["do-3"] + deploy: + replicas: 1 + command: > + /bin/sh -c "fiftyone delegated launch -t remote -m -n teams-do-3" + 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 + 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-3:/tmp/telemetry + + teams-do-3-telemetry: + image: voxel51/telemetry-sidecar:v2.19.0 + profiles: ["do-3"] + pid: "service:teams-do-3" + user: "1000:1000" + cap_drop: + - ALL + cap_add: + - SYS_PTRACE # required for profiling metrics (sampled stacks via py-spy) + security_opt: + - no-new-privileges:true + environment: + POD_NAME: teams-do-3 + 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} + deploy: + resources: + limits: + cpus: "0.10" + memory: 512M + reservations: + cpus: "0.10" + memory: 512M + volumes: + - telemetry-socket-3:/tmp/telemetry + depends_on: + teams-do-3: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + +# tmpfs-backed named volumes with uid/gid set at mount time so the +# non-root sidecar can bind agent.sock without any init container. +# Add new slots with `telemetry-socket-N: *telemetry-socket-spec`. volumes: plugins-vol: + telemetry-socket: &telemetry-socket-spec + driver_opts: + type: tmpfs + device: tmpfs + o: "uid=1000,gid=1000,mode=0755" + telemetry-socket-2: *telemetry-socket-spec + telemetry-socket-3: *telemetry-socket-spec diff --git a/docker/internal-auth/compose.plugins.yaml b/docker/internal-auth/compose.plugins.yaml index 1eefc9b9..7e8d116e 100644 --- a/docker/internal-auth/compose.plugins.yaml +++ b/docker/internal-auth/compose.plugins.yaml @@ -34,5 +34,33 @@ services: file: ../common-services.yaml service: teams-cas-common + telemetry-redis: + extends: + file: ../common-services.yaml + service: telemetry-redis-common + + fiftyone-app-telemetry: + extends: + file: ../common-services.yaml + service: fiftyone-app-telemetry-common + depends_on: + fiftyone-app: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + + teams-api-telemetry: + extends: + file: ../common-services.yaml + service: teams-api-telemetry-common + depends_on: + teams-api: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + volumes: plugins-vol: + telemetry-redis-data: diff --git a/docker/internal-auth/compose.yaml b/docker/internal-auth/compose.yaml index 89658501..99c6ef8d 100644 --- a/docker/internal-auth/compose.yaml +++ b/docker/internal-auth/compose.yaml @@ -25,3 +25,33 @@ services: extends: file: ../common-services.yaml service: teams-cas-common + + telemetry-redis: + extends: + file: ../common-services.yaml + service: telemetry-redis-common + + fiftyone-app-telemetry: + extends: + file: ../common-services.yaml + service: fiftyone-app-telemetry-common + depends_on: + fiftyone-app: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + + teams-api-telemetry: + extends: + file: ../common-services.yaml + service: teams-api-telemetry-common + depends_on: + teams-api: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + +volumes: + telemetry-redis-data: diff --git a/docker/internal-auth/env.template b/docker/internal-auth/env.template index 3933cdaf..b2f22a6c 100644 --- a/docker/internal-auth/env.template +++ b/docker/internal-auth/env.template @@ -94,7 +94,41 @@ FIFTYONE_APP_DEFAULT_QUERY_PERFORMANCE=true # application. Set to false to disable Query Performance mode for entire application. FIFTYONE_APP_ENABLE_QUERY_PERFORMANCE=true -# Number of worker replicas for executing delegated operations with builtin infrastructure. -# 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 +# Delegated-operator worker count is selected via Compose profiles when +# the `compose.delegated-operators.yaml` overlay is in use. Slot 1 +# (`teams-do`) runs by default; uncomment the line below to add slots +# 2 and/or 3, each paired with its own telemetry sidecar (static cap +# of 3). `do-N` includes every lower slot, so `do-3` runs three workers. +# +# See docker/docs/configuring-telemetry.md +# and docker/docs/configuring-delegated-operators.md for details. The +# value must not exceed the max concurrent delegated operators set in +# your license file. + +# ─── Telemetry (bundled by default; overrides below are optional) ────── +# Telemetry (`telemetry-redis` + per-service sidecars) is included in +# every compose file by default. The variables below only need to be set +# when overriding the defaults. See +# docker/docs/configuring-telemetry.md to opt out or swap in an external +# Redis. + +# Redis URL used by the telemetry sidecars and the main fiftyone-app / +# teams-api / teams-app / teams-plugins / teams-do containers. Leave +# blank to use the bundled in-stack Redis (redis://telemetry-redis:6379). +# Override to point at a managed Redis. +# FIFTYONE_TELEMETRY_REDIS_URL= + +# Redis image and max memory for the bundled telemetry backend. The +# defaults mirror the helm `telemetry.redis.*` settings. +# 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_PLUGINS_TARGET_NAME=hypercorn +# TEAMS_DO_TARGET_NAME="fiftyone delegated" diff --git a/docker/legacy-auth/compose.dedicated-plugins.yaml b/docker/legacy-auth/compose.dedicated-plugins.yaml index ace9a32c..7e679a4d 100644 --- a/docker/legacy-auth/compose.dedicated-plugins.yaml +++ b/docker/legacy-auth/compose.dedicated-plugins.yaml @@ -38,5 +38,44 @@ services: volumes: - plugins-vol:/opt/plugins:ro + telemetry-redis: + extends: + file: ../common-services.yaml + service: telemetry-redis-common + + fiftyone-app-telemetry: + extends: + file: ../common-services.yaml + service: fiftyone-app-telemetry-common + depends_on: + fiftyone-app: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + + teams-api-telemetry: + extends: + file: ../common-services.yaml + service: teams-api-telemetry-common + depends_on: + teams-api: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + + teams-plugins-telemetry: + extends: + file: ../common-services.yaml + service: teams-plugins-telemetry-common + depends_on: + teams-plugins: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + 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 new file mode 100644 index 00000000..57008dac --- /dev/null +++ b/docker/legacy-auth/compose.delegated-operators.gpu.yaml @@ -0,0 +1,102 @@ +--- +# 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 \ +# 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: + teams-do-gpu: + image: voxel51/fiftyone-teams-cv-full:v2.19.0 + 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 + FIFTYONE_TELEMETRY_REDIS_URL: ${FIFTYONE_TELEMETRY_REDIS_URL:-redis://telemetry-redis:6379} + NVIDIA_VISIBLE_DEVICES: ${NVIDIA_VISIBLE_DEVICES:-all} + NVIDIA_DRIVER_CAPABILITIES: ${NVIDIA_DRIVER_CAPABILITIES:-compute,utility} + TELEMETRY_SOCKET: /tmp/telemetry/agent.sock + 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: voxel51/telemetry-sidecar:v2.19.0 + pid: "service:teams-do-gpu" + profiles: ["gpu"] + user: "1000:1000" + cap_add: + - SYS_PTRACE # required for profiling metrics (sampled stacks via py-spy) + deploy: + resources: + limits: + cpus: "0.10" + memory: 512M + reservations: + cpus: "0.10" + memory: 512M + 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 + +# Tmpfs-backed named volume with uid/gid set at mount time so the +# non-root sidecar can bind agent.sock. +volumes: + plugins-vol: + telemetry-socket-gpu: + driver_opts: + type: tmpfs + device: tmpfs + o: "uid=1000,gid=1000,mode=0755" diff --git a/docker/legacy-auth/compose.delegated-operators.yaml b/docker/legacy-auth/compose.delegated-operators.yaml index 5337711f..05483aa0 100644 --- a/docker/legacy-auth/compose.delegated-operators.yaml +++ b/docker/legacy-auth/compose.delegated-operators.yaml @@ -1,4 +1,6 @@ --- +# See docker/docs/configuring-telemetry.md for details on telemetry settings. +# # For Proxy Server instructions please see # https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies services: @@ -7,5 +9,161 @@ services: file: ../common-services.yaml service: teams-do-common + teams-do-telemetry: + extends: + file: ../common-services.yaml + service: teams-do-telemetry-common + depends_on: + teams-do: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + + # Slot 2 — included by the `do-2` and `do-3` profiles. Defined inline + # (rather than extending teams-do-common) so it can own its own + # executor-socket named volume; named volumes are shared by their + # volume name, so reusing `telemetry-socket` across slots would + # clobber the socket. `-n teams-do-2` registers this worker as a + # distinct orchestrator name so it surfaces separately in the + # Settings → Metrics UI. + teams-do-2: + image: voxel51/fiftyone-teams-cv-full:v2.19.0 + profiles: ["do-2", "do-3"] + deploy: + replicas: 1 + command: > + /bin/sh -c "fiftyone delegated launch -t remote -m -n teams-do-2" + 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 + 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-2:/tmp/telemetry + + teams-do-2-telemetry: + image: voxel51/telemetry-sidecar:v2.19.0 + profiles: ["do-2", "do-3"] + pid: "service:teams-do-2" + user: "1000:1000" + cap_drop: + - ALL + cap_add: + - SYS_PTRACE # allows for additional profiling metrics (sampled stacks via py-spy) + security_opt: + - no-new-privileges:true + environment: + POD_NAME: teams-do-2 + 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} + deploy: + resources: + limits: + cpus: "0.10" + memory: 512M + reservations: + cpus: "0.10" + memory: 512M + volumes: + - telemetry-socket-2:/tmp/telemetry + depends_on: + teams-do-2: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + + # Slot 3 — included by the `do-3` profile. Mirror of slot 2 with its + # own socket volume and orchestrator name. The cap is 3 by design; + # for higher worker counts use the helm chart, which adds a sidecar + # to every pod automatically. + teams-do-3: + image: voxel51/fiftyone-teams-cv-full:v2.19.0 + profiles: ["do-3"] + deploy: + replicas: 1 + command: > + /bin/sh -c "fiftyone delegated launch -t remote -m -n teams-do-3" + 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 + 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-3:/tmp/telemetry + + teams-do-3-telemetry: + image: voxel51/telemetry-sidecar:v2.19.0 + profiles: ["do-3"] + pid: "service:teams-do-3" + user: "1000:1000" + cap_drop: + - ALL + cap_add: + - SYS_PTRACE # allows for additional profiling metrics (sampled stacks via py-spy) + security_opt: + - no-new-privileges:true + environment: + POD_NAME: teams-do-3 + 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} + deploy: + resources: + limits: + cpus: "0.10" + memory: 512M + reservations: + cpus: "0.10" + memory: 512M + volumes: + - telemetry-socket-3:/tmp/telemetry + depends_on: + teams-do-3: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + restart: always + +# tmpfs-backed named volumes with uid/gid set at mount time so the +# non-root sidecar can bind agent.sock without any init container. +# Add new slots with `telemetry-socket-N: *telemetry-socket-spec`. volumes: plugins-vol: + telemetry-socket: &telemetry-socket-spec + driver_opts: + type: tmpfs + device: tmpfs + o: "uid=1000,gid=1000,mode=0755" + telemetry-socket-2: *telemetry-socket-spec + telemetry-socket-3: *telemetry-socket-spec diff --git a/docker/legacy-auth/compose.plugins.yaml b/docker/legacy-auth/compose.plugins.yaml index c71f2722..89794d4c 100644 --- a/docker/legacy-auth/compose.plugins.yaml +++ b/docker/legacy-auth/compose.plugins.yaml @@ -33,5 +33,33 @@ services: CAS_URL: ${AUTH0_BASE_URL} NEXTAUTH_URL: ${AUTH0_BASE_URL}/cas/api/auth + telemetry-redis: + extends: + file: ../common-services.yaml + service: telemetry-redis-common + + fiftyone-app-telemetry: + extends: + file: ../common-services.yaml + service: fiftyone-app-telemetry-common + depends_on: + fiftyone-app: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + + teams-api-telemetry: + extends: + file: ../common-services.yaml + service: teams-api-telemetry-common + depends_on: + teams-api: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + volumes: plugins-vol: + telemetry-redis-data: diff --git a/docker/legacy-auth/compose.yaml b/docker/legacy-auth/compose.yaml index c463cf25..638e20b8 100644 --- a/docker/legacy-auth/compose.yaml +++ b/docker/legacy-auth/compose.yaml @@ -1,6 +1,8 @@ --- # For Proxy Server instructions please see # https://github.com/voxel51/fiftyone-teams-app-deploy/tree/main/docker#environment-proxies +# +# See docker/docs/configuring-telemetry.md for details on telemetry settings. services: fiftyone-app: extends: @@ -24,3 +26,33 @@ services: extends: file: ../common-services.yaml service: teams-cas-common + + telemetry-redis: + extends: + file: ../common-services.yaml + service: telemetry-redis-common + + fiftyone-app-telemetry: + extends: + file: ../common-services.yaml + service: fiftyone-app-telemetry-common + depends_on: + fiftyone-app: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + + teams-api-telemetry: + extends: + file: ../common-services.yaml + service: teams-api-telemetry-common + depends_on: + teams-api: + condition: service_started + restart: true + telemetry-redis: + condition: service_started + +volumes: + telemetry-redis-data: diff --git a/docker/legacy-auth/env.template b/docker/legacy-auth/env.template index 42c5cc96..2773ce12 100644 --- a/docker/legacy-auth/env.template +++ b/docker/legacy-auth/env.template @@ -99,7 +99,44 @@ FIFTYONE_APP_DEFAULT_QUERY_PERFORMANCE=true # application. Set to false to disable Query Performance mode for entire application. FIFTYONE_APP_ENABLE_QUERY_PERFORMANCE=true -# Number of worker replicas for executing delegated operations with builtin infrastructure. -# 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 +# Delegated-operator worker count is selected via Compose profiles when +# the `compose.delegated-operators.yaml` overlay is in use. Slot 1 +# (`teams-do`) runs by default; uncomment the line below to add slots +# 2 and/or 3, each paired with its own telemetry sidecar (static cap +# of 3). `do-N` includes every lower slot, so `do-3` runs three workers. +# +# This replaces the deprecated `FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS` +# env var. To match its previous default of 3 workers, set +# COMPOSE_PROFILES=do-3 below. See docker/docs/configuring-telemetry.md +# and docker/docs/configuring-delegated-operators.md for details. The +# value must not exceed the max concurrent delegated operators set in +# your license file. For more than 3 workers, use the helm chart. +# COMPOSE_PROFILES=do-3 + +# ─── Telemetry (bundled by default; overrides below are optional) ────── +# Telemetry (`telemetry-redis` + per-service sidecars) is included in +# every compose file by default. The variables below only need to be set +# when overriding the bundled defaults. See +# docker/docs/configuring-telemetry.md to opt out or swap in an external +# Redis. + +# Redis URL used by the telemetry sidecars and the main fiftyone-app / +# teams-api / teams-app / teams-plugins / teams-do containers. Leave +# blank to use the bundled in-stack Redis (redis://telemetry-redis:6379). +# Override to point at a managed Redis (e.g. ElastiCache, MemoryStore). +# FIFTYONE_TELEMETRY_REDIS_URL= + +# Redis image and max memory for the bundled telemetry backend. The +# defaults mirror the helm `telemetry.redis.*` settings. +# 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_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 ce1317c9..d09cad66 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) +- [Telemetry Sidecar](#telemetry-sidecar) - [Refresh Orchestrator Operators](#refresh-orchestrator-operators) - [Additional Considerations](#additional-considerations) - [Credential Rotation](#credential-rotation) @@ -459,6 +460,109 @@ fom.register_orchestrator( ) ``` +## Telemetry Sidecar + +> [!IMPORTANT] +> The telemetry sidecar can be disabled, but doing so disables the +> FiftyOne UI's log viewer for delegated-operator runs — it depends on +> the sidecar to capture per-operation logs. + +If your deployment runs telemetry (the Helm chart includes +it 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. + +We use Kubernetes +[native sidecar](https://kubernetes.io/docs/concepts/workloads/pods/sidecar-containers/) +(an `initContainer` with `restartPolicy: Always`). +A regular sidecar container would block Job completion where the Job stays +in `Running` status 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:v2.19.0 + restartPolicy: Always + securityContext: + # The sidecar image runs as root (SYS_PTRACE + + # /proc//fd/1 access require it). Set explicitly so it + # works even when the pod's podSecurityContext sets + # runAsNonRoot: true. + runAsNonRoot: false + runAsUser: 0 + capabilities: + add: [SYS_PTRACE] + env: + - name: TARGET_NAME + value: "fiftyone 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-teams.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-teams.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. +- `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 +646,59 @@ spec: serviceAccountName: your-org-fiftyone-teams podSecurityContext: runAsNonRoot: false + shareProcessNamespace: true + initContainers: + - name: telemetry-sidecar + image: voxel51/telemetry-sidecar:v2.19.0 + restartPolicy: Always + securityContext: + # The sidecar image runs as root (SYS_PTRACE + + # /proc//fd/1 access require it). Set explicitly so it + # works even when the pod's podSecurityContext sets + # runAsNonRoot: true. + runAsNonRoot: false + runAsUser: 0 + capabilities: + add: [SYS_PTRACE] + env: + - name: TARGET_NAME + value: "fiftyone 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: 100m + memory: 512Mi + requests: + cpu: 100m + memory: 512Mi + volumeMounts: + - mountPath: /tmp/telemetry + name: telemetry-socket containers: - name: task-worker image: registry/image:tag @@ -585,8 +742,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 +779,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 +815,17 @@ spec: medium: Memory sizeLimit: 2Gi name: shm-vol + - emptyDir: {} + name: telemetry-socket restartPolicy: Never ``` + +The `telemetry-sidecar` init container above is optional. +To opt out of running telemetry, remove all of the following from the +template: + +- the `telemetry-sidecar` init container +- `shareProcessNamespace: true` on the Pod spec +- the `TELEMETRY_SOCKET` and `FIFTYONE_TELEMETRY_REDIS_URL` env vars on + `task-worker` +- the `telemetry-socket` volume and its mount on `task-worker` diff --git a/helm/docs/upgrading.md b/helm/docs/upgrading.md index 873e5e93..4cb13fde 100644 --- a/helm/docs/upgrading.md +++ b/helm/docs/upgrading.md @@ -19,6 +19,9 @@ - [Upgrading From Previous Versions](#upgrading-from-previous-versions) - [A Note On Database Migrations](#a-note-on-database-migrations) - [From FiftyOne Enterprise Version 2.0.0 or Higher](#from-fiftyone-enterprise-version-200-or-higher) + - [FiftyOne Enterprise v2.19+ Telemetry Sidecars](#fiftyone-enterprise-v219-telemetry-sidecars) + - [Cluster Requirements](#cluster-requirements) + - [Opting out of Telemetry](#opting-out-of-telemetry) - [FiftyOne Enterprise v2.16+ Additional API Routes](#fiftyone-enterprise-v216-additional-api-routes) - [FiftyOne Enterprise v2.15+ Additional API Routes](#fiftyone-enterprise-v215-additional-api-routes) - [FiftyOne Enterprise v2.9+ Startup Probe Changes](#fiftyone-enterprise-v29-startup-probe-changes) @@ -141,6 +144,74 @@ quickstart 0.21.2 fiftyone migrate --info ``` +#### FiftyOne Enterprise v2.19+ Telemetry Sidecars + +FiftyOne Enterprise v2.19.0 adds observability features viewable by +admins directly in the FiftyOne UI. +These are powered by a `telemetry-sidecar` container injected into the +`teams-api`, `fiftyone-app`, `teams-plugins`, and delegated-operator +workloads (and as a native sidecar on on-demand delegated-operator +`Job` pods), plus an in-cluster Redis `Deployment` + `Service` that +buffers the streamed metrics and logs. + +**Resource impact:** +Each sidecar requests `100m` CPU and `512Mi` memory (request == limit). +A default deploy adds four sidecars +(`teams-api` + `fiftyone-app` + `teams-plugins` + one +delegated-operator), so expect roughly **+400m CPU** and **+2 GiB +memory** in additional resource usage, plus the bundled Redis (`250m` +CPU / `512Mi` memory, request == limit) backed by an `emptyDir`. +Tune via `telemetry.sidecar.resources` and `telemetry.redis.resources`. +Opt into a `PersistentVolumeClaim` with +`telemetry.redis.persistence.enabled: true`. + +##### Cluster Requirements + +1. **`shareProcessNamespace: true`** is set on all four workloads so + the sidecar can read `/proc//fd/1` of the target container. +1. **Sidecars run as UID/GID 1000** (matching the workload image's + user) with `cap_drop: ALL`. The delegated-operator sidecar + additionally has `SYS_PTRACE` added (required for `py-spy` stack + sampling); the other three sidecars run with no capabilities. This + posture is compatible with + [Pod Security Admission][psa] `restricted` out of the box. + Clusters enforcing additional admission policies (OPA/Gatekeeper, + Kyverno) that explicitly block `SYS_PTRACE` on the + delegated-operator namespace can either allow that single capability + or disable telemetry with `telemetry.enabled: false`. + +**External Redis:** +To point at a managed Redis (ElastiCache, MemoryStore, an existing +cluster) instead of running the bundled one, set: + +```yaml +telemetry: + redis: + external: + url: redis://my-managed-redis.example.com:6379 +``` + +The chart skips the bundled Redis' `Deployment` and `Service` and wires +every consumer at this URL. + +##### Opting out of Telemetry + +Telemetry is enabled by default. +To disable it, set the following in your `values.yaml` file: + +```yaml +telemetry: + enabled: false +``` + +> [!IMPORTANT] +> The telemetry sidecars power the FiftyOne UI's delegated-operator log viewer. +> Disabling telemetry (`telemetry.enabled: false`) will leave that log +> viewer empty. + +This skips every telemetry resource (Redis, sidecars, Role/RoleBinding) +and renders the workloads without telemetry containers or env vars. + #### FiftyOne Enterprise v2.16+ Additional API Routes FiftyOne Enterprise v2.16.0 adds the `/cloud_credentials` endpoint to the `teams-api`. @@ -596,3 +667,4 @@ existing configuration to migrate to a new Auth0 Tenant. [init-containers]: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ +[psa]: https://kubernetes.io/docs/concepts/security/pod-security-admission/ diff --git a/helm/fiftyone-teams-app/README.md b/helm/fiftyone-teams-app/README.md index 8b066e31..b801e315 100644 --- a/helm/fiftyone-teams-app/README.md +++ b/helm/fiftyone-teams-app/README.md @@ -745,7 +745,7 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): | apiSettings.podDisruptionBudget | object | `{"enabled":false,"minAvailable":null}` | Pod Disruption Budget for pods for `teams-api`. [Reference][pod-disruption-budget]. | | apiSettings.podDisruptionBudget.enabled | bool | `false` | Whether a pod disruption budget is enabled for `teams-api`. | | apiSettings.podDisruptionBudget.minAvailable | string | `nil` | Sets the minimum available or maximum unavailable replicas for the deployment object. Either integers or percentages supported. `maxUnavailable` is also supported, however, only one setting can be used at a time. If both are set, `minAvailable` will be preferred. | -| apiSettings.podSecurityContext | object | `{}` | Pod-level security attributes and common container settings for `teams-api`. [Reference][security-context]. | +| apiSettings.podSecurityContext | object | `{"fsGroup":1000,"runAsGroup":1000,"runAsNonRoot":true,"runAsUser":1000}` | Pod-level security attributes and common container settings for `teams-api`. [Reference][security-context]. UID/GID 1000 matches the image; set explicitly to satisfy PSA `restricted`. | | apiSettings.rbac | object | `{"create":true,"role":{"annotations":{},"labels":{},"name":""},"roleBinding":{"annotations":{},"create":true,"labels":{},"name":""},"serviceAccount":{"annotations":{},"create":true,"labels":{},"name":""}}` | RBAC roles, bindings, and service accounts which will be used to submit on-demand delegated operators to the kubernetes API. If `apiSettings.rbac.create=true`, these will be used by the `teams-api` pods. | | apiSettings.rbac.create | bool | `true` | Controls whether to create the `Role`, `RoleBinding`, and `ServiceAccount` for on-demand delegated-operator submission. | | apiSettings.rbac.role.annotations | object | `{}` | `Role` annotations. [Reference][annotations]. | @@ -812,7 +812,7 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): | appSettings.podDisruptionBudget | object | `{"enabled":false,"minAvailable":null}` | Pod Disruption Budget for pods for `fiftyone-app`. [Reference][pod-disruption-budget]. | | appSettings.podDisruptionBudget.enabled | bool | `false` | Whether a pod disruption budget is enabled for `fiftyone-app`. | | appSettings.podDisruptionBudget.minAvailable | string | `nil` | Sets the minimum available or maximum unavailable replicas for the deployment object. Either integers or percentages supported. `maxUnavailable` is also supported, however, only one setting can be used at a time. If both are set, `minAvailable` will be preferred. | -| appSettings.podSecurityContext | object | `{}` | Pod-level security attributes and common container settings for `fiftyone-app`. [Reference][security-context]. | +| appSettings.podSecurityContext | object | `{"fsGroup":1000,"runAsGroup":1000,"runAsNonRoot":true,"runAsUser":1000}` | Pod-level security attributes and common container settings for `fiftyone-app`. [Reference][security-context]. UID/GID 1000 matches the image; set explicitly to satisfy PSA `restricted`. | | appSettings.readiness.failureThreshold | int | `5` | Number of times to retry the readiness probe for the `fiftyone-app`. [Reference][probes]. | | appSettings.readiness.periodSeconds | int | `15` | How often (in seconds) to perform the readiness probe for `fiftyone-app`. [Reference][probes]. | | appSettings.readiness.timeoutSeconds | int | `5` | Timeout for the readiness probe for the `fiftyone-app`. [Reference][probes]. | @@ -885,7 +885,7 @@ 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},"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":{"fsGroup":1000,"runAsGroup":1000,"runAsNonRoot":true,"runAsUser":1000},"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. | @@ -903,7 +903,7 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): | 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`. | | delegatedOperatorDeployments.template.podDisruptionBudget.minAvailable | string | `nil` | Sets the minimum available or maximum unavailable replicas for the deployment object. Either integers or percentages supported. `maxUnavailable` is also supported, however, only one setting can be used at a time. If both are set, `minAvailable` will be preferred. | -| delegatedOperatorDeployments.template.podSecurityContext | object | `{}` | Pod-level security attributes and common container settings for `delegated-operator-executor`. [Reference][security-context]. | +| delegatedOperatorDeployments.template.podSecurityContext | object | `{"fsGroup":1000,"runAsGroup":1000,"runAsNonRoot":true,"runAsUser":1000}` | Pod-level security attributes and common container settings for `delegated-operator-executor`. [Reference][security-context]. UID/GID 1000 matches the image; set explicitly to satisfy PSA `restricted`. | | delegatedOperatorDeployments.template.readiness.failureThreshold | int | `5` | Number of times to retry the readiness probe for the `teams-do`. [Reference][probes]. | | delegatedOperatorDeployments.template.readiness.periodSeconds | int | `30` | How often (in seconds) to perform the readiness probe for `teams-do`. [Reference][probes]. | | delegatedOperatorDeployments.template.readiness.timeoutSeconds | int | `30` | Timeout for the readiness probe for the `teams-do`. [Reference][probes]. | @@ -924,7 +924,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},"image":{"pullPolicy":"Always","repository":"voxel51/fiftyone-teams-cv-full","tag":""},"jobAnnotations":{},"labels":{},"nodeSelector":{},"podAnnotations":{},"podSecurityContext":{"fsGroup":1000,"runAsGroup":1000,"runAsNonRoot":true,"runAsUser":1000},"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]. | @@ -939,7 +939,7 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): | delegatedOperatorJobTemplates.template.labels | object | `{}` | Additional labels for the `delegated-operator-executor` related objects. [Reference][labels-and-selectors]. | | 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]. | +| delegatedOperatorJobTemplates.template.podSecurityContext | object | `{"fsGroup":1000,"runAsGroup":1000,"runAsNonRoot":true,"runAsUser":1000}` | Pod-level security attributes and common container settings for `delegated-operator-executor`. [Reference][security-context]. UID/GID 1000 matches the image; set explicitly to satisfy PSA `restricted`. | | delegatedOperatorJobTemplates.template.resources | object | `{"limits":{},"requests":{}}` | Container resource requests and limits for `delegated-operator-executor`. [Reference][resources]. | | delegatedOperatorJobTemplates.template.secretEnv | object | `{}` | Secret variables to be passed to the delegated-operator-executor containers. | | delegatedOperatorJobTemplates.template.tolerations | list | `[]` | Allow the k8s scheduler to schedule delegated-operator-executor pods with matching taints. [Reference][taints-and-tolerations]. | @@ -994,7 +994,7 @@ If pods show unhealthy states (e.g., `0/1`, `CrashLoopBackOff`, `Pending`): | pluginsSettings.podDisruptionBudget | object | `{"enabled":false,"minAvailable":null}` | Pod Disruption Budget for pods for `teams-plugins`. [Reference][pod-disruption-budget]. | | pluginsSettings.podDisruptionBudget.enabled | bool | `false` | Whether a pod disruption budget is enabled for `teams-plugins`. | | pluginsSettings.podDisruptionBudget.minAvailable | string | `nil` | Sets the minimum available or maximum unavailable replicas for the deployment object. Either integers or percentages supported. `maxUnavailable` is also supported, however, only one setting can be used at a time. If both are set, `minAvailable` will be preferred. | -| pluginsSettings.podSecurityContext | object | `{}` | Pod-level security attributes and common container settings for `teams-plugins`. [Reference][security-context]. | +| pluginsSettings.podSecurityContext | object | `{"fsGroup":1000,"runAsGroup":1000,"runAsNonRoot":true,"runAsUser":1000}` | Pod-level security attributes and common container settings for `teams-plugins`. [Reference][security-context]. UID/GID 1000 matches the image; set explicitly to satisfy PSA `restricted`. | | pluginsSettings.readiness.failureThreshold | int | `5` | Number of times to retry the readiness probe for the `teams-plugins`. [Reference][probes]. | | pluginsSettings.readiness.periodSeconds | int | `15` | How often (in seconds) to perform the readiness probe for `teams-plugins`. [Reference][probes]. | | pluginsSettings.readiness.timeoutSeconds | int | `5` | Timeout for the readiness probe for the `teams-plugins`. [Reference][probes]. | @@ -1085,6 +1085,22 @@ 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` | When `false`, no telemetry resources or sidecars are rendered and the FiftyOne UI's delegated-operator log viewer will be empty. | +| telemetry.redis.containerSecurityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true}` | Container-level security attributes for the telemetry Redis. `readOnlyRootFilesystem` is safe because writes go to the `/data` volume. [Reference][container-security-context]. | +| telemetry.redis.external.url | string | `""` | URL of an external Redis to use instead of the bundled one (e.g. `redis://my-redis.example.com:6379`). | +| 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.persistence.enabled | bool | `false` | Whether to provision a `PersistentVolumeClaim` for the bundled telemetry Redis. Defaults to `false` (emptyDir) so the chart installs without a default `StorageClass`. Archives flush to MongoDB every ~10 minutes, so a Redis pod restart only drops the in-window dashboard backscroll and any live op buffers. | +| telemetry.redis.persistence.existingClaim | string | `""` | Name of an existing `PersistentVolumeClaim` (in `namespace.name`) to bind the telemetry Redis to. When set, `size` and `storageClass` above are ignored. Has no effect when `persistence.enabled` is `false`. | +| telemetry.redis.persistence.size | string | `"1Gi"` | Storage size for the telemetry Redis `PersistentVolumeClaim`. | +| telemetry.redis.persistence.storageClass | string | `""` | `StorageClass` name for the telemetry Redis `PersistentVolumeClaim`. Leave unset to use the cluster's default `StorageClass`. | +| telemetry.redis.podSecurityContext | object | `{"fsGroup":999,"runAsGroup":999,"runAsNonRoot":true,"runAsUser":999}` | Pod-level security attributes for the telemetry Redis. UID/GID 999 matches the `redis` user in the `redis:7-alpine` image; `fsGroup` keeps the mounted `/data` volume group-writable. [Reference][security-context]. | +| telemetry.redis.resources | object | `{"limits":{"cpu":"250m","memory":"512Mi"},"requests":{"cpu":"250m","memory":"512Mi"}}` | Resource requests/limits for the telemetry Redis container. [Reference][resources]. | +| telemetry.serviceAccounts | list | `[]` | ServiceAccount names (in `namespace.name`) bound to the telemetry pod-logs Role. When empty, the RoleBinding binds the chart's main app service account. The teams-api sidecar uses the teams-api RBAC service account, which already grants `pods/log` GET via `api-role.yaml`. | +| telemetry.sidecar.image.pullPolicy | string | `"Always"` | Instruct when the kubelet should pull (download) the specified image. One of `IfNotPresent`, `Always` or `Never`. [Reference][image-pull-policy]. | +| telemetry.sidecar.image.repository | string | `"voxel51/telemetry-sidecar"` | Container image for `telemetry-sidecar`. | +| telemetry.sidecar.image.tag | string | `""` | Image tag for `telemetry-sidecar`. Defaults to `Chart.AppVersion`. | +| telemetry.sidecar.resources | object | `{"limits":{"cpu":"100m","memory":"512Mi"},"requests":{"cpu":"100m","memory":"512Mi"}}` | Resource requests/limits for each `telemetry-sidecar` container. | diff --git a/helm/fiftyone-teams-app/templates/NOTES.txt b/helm/fiftyone-teams-app/templates/NOTES.txt index c85b16b5..029569da 100644 --- a/helm/fiftyone-teams-app/templates/NOTES.txt +++ b/helm/fiftyone-teams-app/templates/NOTES.txt @@ -4,6 +4,13 @@ apiSettings.replicaCount will be set to 1 for this deployment. Please see https://helm.fiftyone.ai for details. {{ end }} - +{{- if and .Values.telemetry.enabled (not .Values.telemetry.redis.external.url) (not .Values.telemetry.redis.persistence.enabled) (not .Values.telemetry.redis.persistence.existingClaim) }} +[INFO] Telemetry Redis is running on an emptyDir volume (no PersistentVolumeClaim). + Long-term telemetry archives in MongoDB are unaffected, but the in-window + dashboard backscroll (~10 minutes of un-archived data) is lost if the + Redis pod restarts. To make telemetry survive Redis restarts, set + `telemetry.redis.persistence.enabled: true` (needs a working StorageClass + or `telemetry.redis.persistence.existingClaim` pointing at a pre-created PVC). +{{ end }} Visit the following URL to access your FiftyOne Enterprise application: http{{ if $.Values.ingress.tlsEnabled }}s{{ end }}://{{ .Values.teamsAppSettings.dnsName }} diff --git a/helm/fiftyone-teams-app/templates/_do_targets.tpl b/helm/fiftyone-teams-app/templates/_do_targets.tpl index 7806b82c..1466435a 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: {{ include "telemetry.redis.url" .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 bf4a311c..5bb22d40 100644 --- a/helm/fiftyone-teams-app/templates/_helpers.tpl +++ b/helm/fiftyone-teams-app/templates/_helpers.tpl @@ -324,6 +324,11 @@ Create a merged list of environment variables for delegated-operator-executor secretKeyRef: name: {{ .secretName }} key: encryptionKey +{{- if and .ctx .ctx.Values.telemetry.enabled }} +{{- include "telemetry.redis-url-env" .ctx }} +- name: TELEMETRY_SOCKET + value: /tmp/telemetry/agent.sock +{{- end }} {{- range $key, $val := .env }} - name: {{ $key }} value: {{ $val | quote }} @@ -382,6 +387,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 +433,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 +550,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 +602,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 new file mode 100644 index 00000000..712806ff --- /dev/null +++ b/helm/fiftyone-teams-app/templates/_telemetry.tpl @@ -0,0 +1,206 @@ +{{/* +Name of the telemetry redis Deployment/Service/PVC. +*/}} +{{- define "telemetry.redis.name" -}} +{{- printf "%s-telemetry-redis" .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- end }} + +{{/* +Resolves the URL for the telemetry Redis backend. + +If `telemetry.redis.external.url` is set, returns it (chart skips the +bundled Redis Deployment/Service/PVC and consumer workloads + sidecars +are wired at the external URL instead — e.g. for managed Redis like +ElastiCache or MemoryStore). Otherwise returns the in-cluster Service +URL of the bundled Redis as a fully-qualified `..svc.cluster.local` +hostname so cross-namespace consumers (e.g. delegated-operator Jobs +scheduled into a different namespace) still resolve it. + +Always returns a non-empty URL when telemetry is enabled. +*/}} +{{- define "telemetry.redis.url" -}} +{{- if .Values.telemetry.redis.external.url -}} +{{- .Values.telemetry.redis.external.url -}} +{{- else -}} +{{- printf "redis://%s.%s.svc.cluster.local:6379" (include "telemetry.redis.name" .) .Values.namespace.name -}} +{{- end -}} +{{- 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 }} + +{{/* +Default subjects for the telemetry RoleBinding. When .Values.telemetry.serviceAccounts +is empty, bind to the main app service account — the SA used by the auto-injected +sidecars on app/plugins/delegated-operator pods. + +The teams-api sidecar uses the teams-api RBAC service account, which is already +granted `pods/log` GET by api-role.yaml; binding it here would be a redundant +duplicate. When `apiSettings.rbac.create` is false, api-deployment falls back to +the main app SA anyway, which this RoleBinding still covers. +*/}} +{{- define "telemetry.role.subjects" -}} +{{- $appSA := include "fiftyone-teams-app.serviceAccountName" . | trim -}} +{{- $defaultSubjects := list $appSA -}} +{{- $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: {{ include "telemetry.redis.url" .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.repository }}:{{ .ctx.Values.telemetry.sidecar.image.tag | default .ctx.Chart.AppVersion }}" + imagePullPolicy: {{ .ctx.Values.telemetry.sidecar.image.pullPolicy | default "Always" }} + env: + {{- include "telemetry.sidecar-env" . | nindent 4 }} + {{- with .ctx.Values.telemetry.sidecar.resources }} + resources: + {{- toYaml . | nindent 4 }} + {{- end }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + {{- if .executor }} + add: ["SYS_PTRACE"] + {{- end }} + {{- if .executor }} + volumeMounts: + - name: telemetry-socket + mountPath: /tmp/telemetry + {{- end }} +{{- end }} + +{{/* +A native-sidecar (initContainer with restartPolicy: Always) variant — for use under +spec.initContainers in DO Jobs, where a regular sidecar container that does not exit +would block Job completion. +*/}} +{{- define "telemetry.native-sidecar" -}} +- name: telemetry-sidecar + image: "{{ .ctx.Values.telemetry.sidecar.image.repository }}:{{ .ctx.Values.telemetry.sidecar.image.tag | default .ctx.Chart.AppVersion }}" + imagePullPolicy: {{ .ctx.Values.telemetry.sidecar.image.pullPolicy | 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: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + {{- if .executor }} + add: ["SYS_PTRACE"] + {{- end }} + 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: {{ include "telemetry.redis.url" . | quote }} +{{- end }} +{{- end }} diff --git a/helm/fiftyone-teams-app/templates/api-deployment.yaml b/helm/fiftyone-teams-app/templates/api-deployment.yaml index a0623130..04ce551c 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.telemetry.enabled }} + shareProcessNamespace: true + {{- end }} securityContext: {{- toYaml .Values.apiSettings.podSecurityContext | nindent 8 }} containers: @@ -89,6 +92,9 @@ spec: {{- with .Values.apiSettings.volumeMounts }} {{- toYaml . | nindent 12 }} {{- end }} + {{- if .Values.telemetry.enabled }} + {{- include "telemetry.sidecar" (dict "ctx" . "serviceType" "teams-api" "targetName" "fiftyone-teams-api") | nindent 8 }} + {{- end }} {{- if .Values.apiSettings.initContainers.enabled }} initContainers: {{- diff --git a/helm/fiftyone-teams-app/templates/api-role.yaml b/helm/fiftyone-teams-app/templates/api-role.yaml index def64ff6..fe5bd00b 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/app-deployment.yaml b/helm/fiftyone-teams-app/templates/app-deployment.yaml index 54d3309f..03ca186f 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.telemetry.enabled }} + shareProcessNamespace: true + {{- end }} securityContext: {{- toYaml .Values.appSettings.podSecurityContext | nindent 8 }} containers: @@ -73,6 +76,9 @@ spec: volumeMounts: {{- toYaml . | nindent 12 }} {{- end }} + {{- if .Values.telemetry.enabled }} + {{- include "telemetry.sidecar" (dict "ctx" . "serviceType" "fiftyone-app" "targetName" "hypercorn") | 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 ca93b52c..cea342aa 100644 --- a/helm/fiftyone-teams-app/templates/delegated-operator-instance-deployment.yaml +++ b/helm/fiftyone-teams-app/templates/delegated-operator-instance-deployment.yaml @@ -11,10 +11,29 @@ "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 }} {{- $updateStrategy := merge (dict) ($v.updateStrategy|default dict) ($baseTpl.updateStrategy) }} +{{- $mergedVolumeMounts := $v.volumeMounts | default $baseTpl.volumeMounts | default list }} +{{- $mergedVolumes := $v.volumes | default $baseTpl.volumes | default list }} +{{- if $.Values.telemetry.enabled }} + {{- $hasSocketVolume := false }} + {{- range $vol := $mergedVolumes }} + {{- if eq $vol.name "telemetry-socket" }}{{- $hasSocketVolume = true }}{{- end }} + {{- end }} + {{- if not $hasSocketVolume }} + {{- $mergedVolumes = append $mergedVolumes (dict "name" "telemetry-socket" "emptyDir" dict) }} + {{- end }} + {{- $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 }} apiVersion: apps/v1 kind: Deployment metadata: @@ -50,6 +69,9 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ include "fiftyone-teams-app.serviceAccountName" $ }} + {{- if $.Values.telemetry.enabled }} + shareProcessNamespace: true + {{- end }} securityContext: {{- toYaml (merge (dict) ($v.podSecurityContext | default dict) ($baseTpl.podSecurityContext)) | nindent 8 }} containers: @@ -75,7 +97,7 @@ 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 }} + {{- with $mergedVolumeMounts }} volumeMounts: {{- toYaml . | nindent 12 }} {{- end }} @@ -106,6 +128,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 }} + {{- if $.Values.telemetry.enabled }} + {{- include "telemetry.sidecar" (dict "ctx" $ "serviceType" "delegated-operator" "targetName" "fiftyone delegated" "executor" true) | nindent 8 }} + {{- end }} {{- with (merge (dict) ($v.nodeSelector | default dict) ($baseTpl.nodeSelector)) }} nodeSelector: {{- toYaml . | nindent 8 }} @@ -129,7 +154,7 @@ spec: ) | nindent 8 }} {{- end }} - {{- with $v.volumes | default $baseTpl.volumes }} + {{- 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 3ce459bd..23a9b208 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. */ -}} @@ -40,8 +41,26 @@ data: {{- $mergedPodSecurityContext := merge (deepCopy ($jobConfig.podSecurityContext | default dict)) ($baseTpl.podSecurityContext | default dict) }} {{- $mergedResources := merge (deepCopy ($jobConfig.resources | default dict)) ($baseTpl.resources) }} {{- $mergedTolerations := $jobConfig.tolerations | default $baseTpl.tolerations }} - {{- $mergedVolumeMounts := $jobConfig.volumeMounts | default $baseTpl.volumeMounts }} - {{- $mergedVolumes := $jobConfig.volumes | default $baseTpl.volumes }} + {{- $mergedVolumeMounts := $jobConfig.volumeMounts | default $baseTpl.volumeMounts | default list }} + {{- $mergedVolumes := $jobConfig.volumes | default $baseTpl.volumes | default list }} + + {{- if $.Values.telemetry.enabled }} + {{- $hasSocketVolume := false }} + {{- range $vol := $mergedVolumes }} + {{- if eq $vol.name "telemetry-socket" }}{{- $hasSocketVolume = true }}{{- end }} + {{- end }} + {{- if not $hasSocketVolume }} + {{- $mergedVolumes = append $mergedVolumes (dict "name" "telemetry-socket" "emptyDir" dict) }} + {{- end }} + {{- $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 }} + {{- /* Trunc at 48 for the random ID to be generated via API */ -}} {{- $metadataName := kebabcase $jobName | trunc 48 }} @@ -87,6 +106,9 @@ data: spec: restartPolicy: {{ $jobConfig.restartPolicy | default $baseTpl.restartPolicy | default "Never" }} serviceAccountName: {{ include "fiftyone-teams-app.serviceAccountName" $ }} + {{- if $.Values.telemetry.enabled }} + shareProcessNamespace: true + {{- end }} {{- with $mergedPodSecurityContext }} securityContext: {{- toYaml . | nindent 12 }} @@ -119,6 +141,10 @@ data: volumeMounts: {{- toYaml . | nindent 16 }} {{- end }} + {{- if $.Values.telemetry.enabled }} + initContainers: + {{- include "telemetry.native-sidecar" (dict "ctx" $ "serviceType" "delegated-operator" "targetName" "fiftyone delegated" "executor" true "targetContainer" "executor") | nindent 12 }} + {{- end }} {{- with $mergedNodeSelector }} nodeSelector: {{- toYaml . | nindent 12 }} diff --git a/helm/fiftyone-teams-app/templates/plugins-deployment.yaml b/helm/fiftyone-teams-app/templates/plugins-deployment.yaml index 85ef25c9..b564f9d6 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.telemetry.enabled }} + shareProcessNamespace: true + {{- end }} securityContext: {{- toYaml .Values.pluginsSettings.podSecurityContext | nindent 8 }} containers: @@ -74,6 +77,9 @@ spec: volumeMounts: {{- toYaml . | nindent 12 }} {{- end }} + {{- if .Values.telemetry.enabled }} + {{- include "telemetry.sidecar" (dict "ctx" . "serviceType" "teams-plugins" "targetName" "hypercorn") | nindent 8 }} + {{- end }} {{- if .Values.pluginsSettings.initContainers.enabled }} initContainers: {{- diff --git a/helm/fiftyone-teams-app/templates/telemetry-redis-deployment.yaml b/helm/fiftyone-teams-app/templates/telemetry-redis-deployment.yaml new file mode 100644 index 00000000..a3a30bb5 --- /dev/null +++ b/helm/fiftyone-teams-app/templates/telemetry-redis-deployment.yaml @@ -0,0 +1,60 @@ +{{- if and .Values.telemetry.enabled (not .Values.telemetry.redis.external.url) }} +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 + strategy: + type: Recreate + selector: + matchLabels: + {{- include "telemetry.redis.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "telemetry.redis.labels" . | nindent 8 }} + spec: + {{- with .Values.telemetry.redis.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: redis + image: {{ .Values.telemetry.redis.image | default "redis:7-alpine" }} + {{- with .Values.telemetry.redis.containerSecurityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + args: + - redis-server + - --save + - "60" + - "1" + - --dir + - /data + - --maxmemory + - {{ .Values.telemetry.redis.maxmemory | default "400mb" | quote }} + - --maxmemory-policy + - allkeys-lru + ports: + - containerPort: 6379 + {{- with .Values.telemetry.redis.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: redis-data + mountPath: /data + volumes: + - name: redis-data + {{- if .Values.telemetry.redis.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ .Values.telemetry.redis.persistence.existingClaim | default (printf "%s-data" (include "telemetry.redis.name" .)) }} + {{- else }} + emptyDir: {} + {{- end }} +{{- end }} diff --git a/helm/fiftyone-teams-app/templates/telemetry-redis-pvc.yaml b/helm/fiftyone-teams-app/templates/telemetry-redis-pvc.yaml new file mode 100644 index 00000000..8b0b0de5 --- /dev/null +++ b/helm/fiftyone-teams-app/templates/telemetry-redis-pvc.yaml @@ -0,0 +1,20 @@ +{{- if and .Values.telemetry.enabled (not .Values.telemetry.redis.external.url) }} +{{- if and .Values.telemetry.redis.persistence.enabled (not .Values.telemetry.redis.persistence.existingClaim) }} +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 + {{- with .Values.telemetry.redis.persistence.storageClass }} + storageClassName: {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.telemetry.redis.persistence.size | default "1Gi" }} +{{- end }} +{{- end }} diff --git a/helm/fiftyone-teams-app/templates/telemetry-redis-service.yaml b/helm/fiftyone-teams-app/templates/telemetry-redis-service.yaml new file mode 100644 index 00000000..c2f3c573 --- /dev/null +++ b/helm/fiftyone-teams-app/templates/telemetry-redis-service.yaml @@ -0,0 +1,15 @@ +{{- if and .Values.telemetry.enabled (not .Values.telemetry.redis.external.url) }} +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-role.yaml b/helm/fiftyone-teams-app/templates/telemetry-role.yaml new file mode 100644 index 00000000..1d5bf77a --- /dev/null +++ b/helm/fiftyone-teams-app/templates/telemetry-role.yaml @@ -0,0 +1,16 @@ +{{- 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"] +{{- 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 00000000..c0af7f2e --- /dev/null +++ b/helm/fiftyone-teams-app/templates/telemetry-rolebinding.yaml @@ -0,0 +1,15 @@ +{{- if .Values.telemetry.enabled }} +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: + {{- 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 0e25880c..5130a014 100644 --- a/helm/fiftyone-teams-app/values.schema.json +++ b/helm/fiftyone-teams-app/values.schema.json @@ -367,7 +367,29 @@ "type": "object" }, "podSecurityContext": { - "description": "Pod-level security attributes and common container settings for `teams-api`. [Reference][security-context].", + "description": "Pod-level security attributes and common container settings for `teams-api`. [Reference][security-context].\nUID/GID 1000 matches the image; set explicitly to satisfy PSA `restricted`.", + "properties": { + "fsGroup": { + "default": 1000, + "title": "fsGroup", + "type": "integer" + }, + "runAsGroup": { + "default": 1000, + "title": "runAsGroup", + "type": "integer" + }, + "runAsNonRoot": { + "default": true, + "title": "runAsNonRoot", + "type": "boolean" + }, + "runAsUser": { + "default": 1000, + "title": "runAsUser", + "type": "integer" + } + }, "required": [], "title": "podSecurityContext", "type": "object" @@ -1055,7 +1077,29 @@ "type": "object" }, "podSecurityContext": { - "description": "Pod-level security attributes and common container settings for `fiftyone-app`. [Reference][security-context].", + "description": "Pod-level security attributes and common container settings for `fiftyone-app`. [Reference][security-context].\nUID/GID 1000 matches the image; set explicitly to satisfy PSA `restricted`.", + "properties": { + "fsGroup": { + "default": 1000, + "title": "fsGroup", + "type": "integer" + }, + "runAsGroup": { + "default": 1000, + "title": "runAsGroup", + "type": "integer" + }, + "runAsNonRoot": { + "default": true, + "title": "runAsNonRoot", + "type": "boolean" + }, + "runAsUser": { + "default": 1000, + "title": "runAsUser", + "type": "integer" + } + }, "required": [], "title": "podSecurityContext", "type": "object" @@ -2129,7 +2173,29 @@ "type": "object" }, "podSecurityContext": { - "description": "Pod-level security attributes and common container settings for `delegated-operator-executor`. [Reference][security-context].", + "description": "Pod-level security attributes and common container settings for `delegated-operator-executor`. [Reference][security-context].\nUID/GID 1000 matches the image; set explicitly to satisfy PSA `restricted`.", + "properties": { + "fsGroup": { + "default": 1000, + "title": "fsGroup", + "type": "integer" + }, + "runAsGroup": { + "default": 1000, + "title": "runAsGroup", + "type": "integer" + }, + "runAsNonRoot": { + "default": true, + "title": "runAsNonRoot", + "type": "boolean" + }, + "runAsUser": { + "default": 1000, + "title": "runAsUser", + "type": "integer" + } + }, "required": [], "title": "podSecurityContext", "type": "object" @@ -2445,7 +2511,29 @@ "type": "object" }, "podSecurityContext": { - "description": "Pod-level security attributes and common container settings for `delegated-operator-executor`. [Reference][security-context].", + "description": "Pod-level security attributes and common container settings for `delegated-operator-executor`. [Reference][security-context].\nUID/GID 1000 matches the image; set explicitly to satisfy PSA `restricted`.", + "properties": { + "fsGroup": { + "default": 1000, + "title": "fsGroup", + "type": "integer" + }, + "runAsGroup": { + "default": 1000, + "title": "runAsGroup", + "type": "integer" + }, + "runAsNonRoot": { + "default": true, + "title": "runAsNonRoot", + "type": "boolean" + }, + "runAsUser": { + "default": 1000, + "title": "runAsUser", + "type": "integer" + } + }, "required": [], "title": "podSecurityContext", "type": "object" @@ -3052,7 +3140,29 @@ "type": "object" }, "podSecurityContext": { - "description": "Pod-level security attributes and common container settings for `teams-plugins`. [Reference][security-context].", + "description": "Pod-level security attributes and common container settings for `teams-plugins`. [Reference][security-context].\nUID/GID 1000 matches the image; set explicitly to satisfy PSA `restricted`.", + "properties": { + "fsGroup": { + "default": 1000, + "title": "fsGroup", + "type": "integer" + }, + "runAsGroup": { + "default": 1000, + "title": "runAsGroup", + "type": "integer" + }, + "runAsNonRoot": { + "default": true, + "title": "runAsNonRoot", + "type": "boolean" + }, + "runAsUser": { + "default": 1000, + "title": "runAsUser", + "type": "integer" + } + }, "required": [], "title": "podSecurityContext", "type": "object" @@ -3928,6 +4038,287 @@ "required": [], "title": "teamsAppSettings", "type": "object" + }, + "telemetry": { + "description": "In-cluster telemetry. Renders a Redis Deployment, Service, a Role/RoleBinding\ngranting `pods/log` read access, and a `telemetry-sidecar` injected into the\n`teams-api`, `fiftyone-app`, `teams-plugins`, and delegated-operator workloads.", + "properties": { + "enabled": { + "default": true, + "description": "When `false`, no telemetry resources or sidecars are rendered and the\nFiftyOne UI's delegated-operator log viewer will be empty.", + "title": "enabled", + "type": "boolean" + }, + "redis": { + "properties": { + "containerSecurityContext": { + "description": "Container-level security attributes for the telemetry Redis.\n`readOnlyRootFilesystem` is safe because writes go to the `/data`\nvolume. [Reference][container-security-context].", + "properties": { + "allowPrivilegeEscalation": { + "default": false, + "title": "allowPrivilegeEscalation", + "type": "boolean" + }, + "capabilities": { + "properties": { + "drop": { + "items": { + "anyOf": [ + { + "type": "string" + } + ], + "required": [] + }, + "title": "drop", + "type": "array" + } + }, + "required": [], + "title": "capabilities", + "type": "object" + }, + "readOnlyRootFilesystem": { + "default": true, + "title": "readOnlyRootFilesystem", + "type": "boolean" + } + }, + "required": [], + "title": "containerSecurityContext", + "type": "object" + }, + "external": { + "description": "When `external.url` is set, the bundled Redis `Deployment`/`Service`/\n`PersistentVolumeClaim` are not rendered; workloads and sidecars wire to\nthe external URL instead (e.g. managed Redis or a shared instance).", + "properties": { + "url": { + "default": "", + "description": "URL of an external Redis to use instead of the bundled one\n(e.g. `redis://my-redis.example.com:6379`).", + "title": "url", + "type": "string" + } + }, + "required": [], + "title": "external", + "type": "object" + }, + "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" + }, + "persistence": { + "properties": { + "enabled": { + "default": false, + "description": "Whether to provision a `PersistentVolumeClaim` for the bundled\ntelemetry Redis. Defaults to `false` (emptyDir) so the chart installs\nwithout a default `StorageClass`. Archives flush to MongoDB every\n~10 minutes, so a Redis pod restart only drops the in-window dashboard\nbackscroll and any live op buffers.", + "title": "enabled", + "type": "boolean" + }, + "existingClaim": { + "default": "", + "description": "Name of an existing `PersistentVolumeClaim` (in `namespace.name`) to\nbind the telemetry Redis to. When set, `size` and `storageClass` above\nare ignored. Has no effect when `persistence.enabled` is `false`.", + "title": "existingClaim", + "type": "string" + }, + "size": { + "default": "1Gi", + "description": "Storage size for the telemetry Redis `PersistentVolumeClaim`.", + "title": "size", + "type": "string" + }, + "storageClass": { + "default": "", + "description": "`StorageClass` name for the telemetry Redis `PersistentVolumeClaim`.\nLeave unset to use the cluster's default `StorageClass`.", + "required": [], + "title": "storageClass", + "type": [ + "string", + "null" + ] + } + }, + "required": [], + "title": "persistence", + "type": "object" + }, + "podSecurityContext": { + "description": "Pod-level security attributes for the telemetry Redis. UID/GID 999\nmatches the `redis` user in the `redis:7-alpine` image; `fsGroup` keeps\nthe mounted `/data` volume group-writable. [Reference][security-context].", + "properties": { + "fsGroup": { + "default": 999, + "title": "fsGroup", + "type": "integer" + }, + "runAsGroup": { + "default": 999, + "title": "runAsGroup", + "type": "integer" + }, + "runAsNonRoot": { + "default": true, + "title": "runAsNonRoot", + "type": "boolean" + }, + "runAsUser": { + "default": 999, + "title": "runAsUser", + "type": "integer" + } + }, + "required": [], + "title": "podSecurityContext", + "type": "object" + }, + "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": "250m", + "title": "cpu", + "type": "string" + }, + "memory": { + "default": "512Mi", + "title": "memory", + "type": "string" + } + }, + "required": [], + "title": "requests", + "type": "object" + } + }, + "required": [], + "title": "resources", + "type": "object" + } + }, + "required": [], + "title": "redis", + "type": "object" + }, + "serviceAccounts": { + "description": "ServiceAccount names (in `namespace.name`) bound to the telemetry\npod-logs Role. When empty, the RoleBinding binds the chart's main app\nservice account. The teams-api sidecar uses the teams-api RBAC service\naccount, which already grants `pods/log` GET via `api-role.yaml`.", + "items": { + "required": [] + }, + "title": "serviceAccounts", + "type": "array" + }, + "sidecar": { + "properties": { + "image": { + "properties": { + "pullPolicy": { + "default": "Always", + "description": "Instruct when the kubelet should pull (download) the specified image.\nOne of `IfNotPresent`, `Always` or `Never`. [Reference][image-pull-policy].", + "oneOf": [ + { + "enum": [ + "Always", + "IfNotPresent", + "Never" + ], + "required": [] + } + ], + "required": [], + "title": "pullPolicy" + }, + "repository": { + "default": "voxel51/telemetry-sidecar", + "description": "Container image for `telemetry-sidecar`.", + "title": "repository", + "type": "string" + }, + "tag": { + "default": "", + "description": "Image tag for `telemetry-sidecar`. Defaults to `Chart.AppVersion`.", + "title": "tag", + "type": "string" + } + }, + "required": [], + "title": "image", + "type": "object" + }, + "resources": { + "description": "Resource requests/limits for each `telemetry-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": [], + "title": "telemetry", + "type": "object" } }, "required": [], diff --git a/helm/fiftyone-teams-app/values.yaml b/helm/fiftyone-teams-app/values.yaml index 890eb603..4e1f7606 100644 --- a/helm/fiftyone-teams-app/values.yaml +++ b/helm/fiftyone-teams-app/values.yaml @@ -191,7 +191,12 @@ apiSettings: minAvailable: # maxUnavailable: "" # -- Pod-level security attributes and common container settings for `teams-api`. [Reference][security-context]. - podSecurityContext: {} + # UID/GID 1000 matches the image; set explicitly to satisfy PSA `restricted`. + podSecurityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 # -- RBAC roles, bindings, and service accounts which will be used to # submit on-demand delegated operators to the kubernetes API. # If `apiSettings.rbac.create=true`, these will be used by the `teams-api` pods. @@ -458,7 +463,12 @@ appSettings: minAvailable: # maxUnavailable: "" # -- Pod-level security attributes and common container settings for `fiftyone-app`. [Reference][security-context]. - podSecurityContext: {} + # UID/GID 1000 matches the image; set explicitly to satisfy PSA `restricted`. + podSecurityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 readiness: # -- Number of times to retry the readiness probe for the `fiftyone-app`. [Reference][probes]. failureThreshold: 5 @@ -788,7 +798,12 @@ delegatedOperatorDeployments: minAvailable: # maxUnavailable: "" # -- Pod-level security attributes and common container settings for `delegated-operator-executor`. [Reference][security-context]. - podSecurityContext: {} + # UID/GID 1000 matches the image; set explicitly to satisfy PSA `restricted`. + podSecurityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 readiness: # -- Number of times to retry the readiness probe for the `teams-do`. [Reference][probes]. failureThreshold: 5 @@ -972,7 +987,12 @@ delegatedOperatorJobTemplates: # -- Annotations for delegated-operator-executor pods. [Reference][annotations]. podAnnotations: {} # -- Pod-level security attributes and common container settings for `delegated-operator-executor`. [Reference][security-context]. - podSecurityContext: {} + # UID/GID 1000 matches the image; set explicitly to satisfy PSA `restricted`. + podSecurityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 # Instead of setting default resources, we require the user define them # This enables running on resource constrained environments # (like Minikube). To set resources, uncomment the following @@ -1295,7 +1315,12 @@ pluginsSettings: minAvailable: # maxUnavailable: "" # -- Pod-level security attributes and common container settings for `teams-plugins`. [Reference][security-context]. - podSecurityContext: {} + # UID/GID 1000 matches the image; set explicitly to satisfy PSA `restricted`. + podSecurityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 readiness: # -- Number of times to retry the readiness probe for the `teams-plugins`. [Reference][probes]. failureThreshold: 5 @@ -1595,3 +1620,92 @@ teamsAppSettings: volumeMounts: [] # -- Volumes for `teams-app` pods. [Reference][volumes]. volumes: [] + +# In-cluster telemetry. Renders a Redis Deployment, Service, a Role/RoleBinding +# granting `pods/log` read access, and a `telemetry-sidecar` injected into the +# `teams-api`, `fiftyone-app`, `teams-plugins`, and delegated-operator workloads. +telemetry: + # -- When `false`, no telemetry resources or sidecars are rendered and the + # FiftyOne UI's delegated-operator log viewer will be empty. + enabled: true + redis: + # -- Container image for the telemetry Redis Deployment. + image: redis:7-alpine + persistence: + # -- Whether to provision a `PersistentVolumeClaim` for the bundled + # telemetry Redis. Defaults to `false` (emptyDir) so the chart installs + # without a default `StorageClass`. Archives flush to MongoDB every + # ~10 minutes, so a Redis pod restart only drops the in-window dashboard + # backscroll and any live op buffers. + enabled: false + # -- Storage size for the telemetry Redis `PersistentVolumeClaim`. + size: 1Gi + # @schema + # type: ["string", "null"] + # @schema + # -- `StorageClass` name for the telemetry Redis `PersistentVolumeClaim`. + # Leave unset to use the cluster's default `StorageClass`. + storageClass: "" + # -- Name of an existing `PersistentVolumeClaim` (in `namespace.name`) to + # bind the telemetry Redis to. When set, `size` and `storageClass` above + # are ignored. Has no effect when `persistence.enabled` is `false`. + existingClaim: "" + # -- `--maxmemory` flag passed to `redis-server`. + maxmemory: 400mb + # -- Resource requests/limits for the telemetry Redis container. [Reference][resources]. + resources: + requests: + cpu: 250m + memory: 512Mi + limits: + cpu: 250m + memory: 512Mi + # -- Pod-level security attributes for the telemetry Redis. UID/GID 999 + # matches the `redis` user in the `redis:7-alpine` image; `fsGroup` keeps + # the mounted `/data` volume group-writable. [Reference][security-context]. + podSecurityContext: + runAsNonRoot: true + runAsUser: 999 + runAsGroup: 999 + fsGroup: 999 + # -- Container-level security attributes for the telemetry Redis. + # `readOnlyRootFilesystem` is safe because writes go to the `/data` + # volume. [Reference][container-security-context]. + containerSecurityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: ["ALL"] + # When `external.url` is set, the bundled Redis `Deployment`/`Service`/ + # `PersistentVolumeClaim` are not rendered; workloads and sidecars wire to + # the external URL instead (e.g. managed Redis or a shared instance). + external: + # -- URL of an external Redis to use instead of the bundled one + # (e.g. `redis://my-redis.example.com:6379`). + url: "" + sidecar: + image: + # @schema + # oneOf: + # - enum: ["Always", "IfNotPresent", "Never"] + # @schema + # -- Instruct when the kubelet should pull (download) the specified image. + # One of `IfNotPresent`, `Always` or `Never`. [Reference][image-pull-policy]. + pullPolicy: Always + # -- Container image for `telemetry-sidecar`. + repository: voxel51/telemetry-sidecar + # -- Image tag for `telemetry-sidecar`. Defaults to `Chart.AppVersion`. + tag: "" + # -- Resource requests/limits for each `telemetry-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. When empty, the RoleBinding binds the chart's main app + # service account. The teams-api sidecar uses the teams-api RBAC service + # account, which already grants `pods/log` GET via `api-role.yaml`. + serviceAccounts: [] diff --git a/tests/fixtures/docker/.env b/tests/fixtures/docker/.env index 004b591f..dae9532c 100644 --- a/tests/fixtures/docker/.env +++ b/tests/fixtures/docker/.env @@ -8,5 +8,3 @@ FIFTYONE_ENCRYPTION_KEY="test-fiftyone-encryption-key" # Internal Auth FIFTYONE_AUTH_SECRET="test-fiftyone-auth-secret" - -FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS=1 diff --git a/tests/fixtures/docker/compose.override.mongodb.yaml b/tests/fixtures/docker/compose.override.mongodb.yaml index 40678940..a853438f 100644 --- a/tests/fixtures/docker/compose.override.mongodb.yaml +++ b/tests/fixtures/docker/compose.override.mongodb.yaml @@ -11,6 +11,10 @@ services: teams-cas: depends_on: [mongodb] image: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-teams-cas:${FIFTYONE_TEAMS_CAS_VERSION} + fiftyone-app-telemetry: + image: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/telemetry-sidecar:${FIFTYONE_APP_VERSION} + teams-api-telemetry: + image: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/telemetry-sidecar:${FIFTYONE_APP_VERSION} mongodb: image: "mongo:8.0.0" restart: always diff --git a/tests/fixtures/docker/compose.override.mongodb_do.yaml b/tests/fixtures/docker/compose.override.mongodb_do.yaml index a9d83928..76770b55 100644 --- a/tests/fixtures/docker/compose.override.mongodb_do.yaml +++ b/tests/fixtures/docker/compose.override.mongodb_do.yaml @@ -2,3 +2,5 @@ services: teams-do: depends_on: [mongodb] image: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app:${FIFTYONE_APP_VERSION} + teams-do-telemetry: + image: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/telemetry-sidecar:${FIFTYONE_APP_VERSION} diff --git a/tests/fixtures/docker/compose.override.mongodb_plugins.yaml b/tests/fixtures/docker/compose.override.mongodb_plugins.yaml index 0e28fbd0..c4318c2f 100644 --- a/tests/fixtures/docker/compose.override.mongodb_plugins.yaml +++ b/tests/fixtures/docker/compose.override.mongodb_plugins.yaml @@ -2,3 +2,5 @@ services: teams-plugins: depends_on: [mongodb] image: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app:${FIFTYONE_APP_VERSION} + teams-plugins-telemetry: + image: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/telemetry-sidecar:${FIFTYONE_APP_VERSION} diff --git a/tests/fixtures/docker/integration_internal_auth.env b/tests/fixtures/docker/integration_internal_auth.env index 48e46857..e93f9ab5 100644 --- a/tests/fixtures/docker/integration_internal_auth.env +++ b/tests/fixtures/docker/integration_internal_auth.env @@ -31,10 +31,8 @@ FIFTYONE_ENV=development CAS_DEBUG="cas:*" # Image tags for existing artifacts -VERSION=v2.19.0rc5 -FIFTYONE_APP_VERSION=v2.19.0rc5 -FIFTYONE_TEAMS_API_VERSION=v2.19.0rc5 -FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.4 -FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.4 - -FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS=1 +VERSION=v2.19.0rc18 +FIFTYONE_APP_VERSION=v2.19.0rc18 +FIFTYONE_TEAMS_API_VERSION=v2.19.0rc18 +FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.17 +FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.17 diff --git a/tests/fixtures/docker/integration_legacy_auth.env b/tests/fixtures/docker/integration_legacy_auth.env index f1728dbd..71fcddd2 100644 --- a/tests/fixtures/docker/integration_legacy_auth.env +++ b/tests/fixtures/docker/integration_legacy_auth.env @@ -39,10 +39,8 @@ FIFTYONE_ENV=development CAS_DEBUG="cas:*" # Image tags for existing artifacts -VERSION=v2.19.0rc5 -FIFTYONE_APP_VERSION=v2.19.0rc5 -FIFTYONE_TEAMS_API_VERSION=v2.19.0rc5 -FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.4 -FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.4 - -FIFTYONE_DELEGATED_OPERATOR_WORKER_REPLICAS=1 +VERSION=v2.19.0rc18 +FIFTYONE_APP_VERSION=v2.19.0rc18 +FIFTYONE_TEAMS_API_VERSION=v2.19.0rc18 +FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.17 +FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.17 diff --git a/tests/fixtures/helm/integration_values.yaml b/tests/fixtures/helm/integration_values.yaml index cb75b126..3f17d048 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.0rc5 + tag: v2.19.0rc18 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.0rc5 + tag: v2.19.0rc18 # 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.4 + tag: v2.19.0-rc.17 delegatedOperatorDeployments: deployments: teamsDoCpuDefault: image: repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.19.0rc5 + tag: v2.19.0rc18 delegatedOperatorTemplates: jobs: teamsDoCpuDefaultK8s: image: repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.19.0rc5 + tag: v2.19.0rc18 ingress: annotations: # For using the nginx-ingress controller with cert-manager self signed certificates @@ -121,7 +121,13 @@ 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.0rc5 + tag: v2.19.0rc18 +telemetry: + sidecar: + image: + repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/telemetry-sidecar + pullPolicy: IfNotPresent + tag: v2.19.0rc18 teamsAppSettings: dnsName: local.fiftyone.ai # env: @@ -138,4 +144,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.4 + tag: v2.19.0-rc.17 diff --git a/tests/integration/compose/docker-compose-internal-auth_test.go b/tests/integration/compose/docker-compose-internal-auth_test.go index d20cc67b..01496634 100644 --- a/tests/integration/compose/docker-compose-internal-auth_test.go +++ b/tests/integration/compose/docker-compose-internal-auth_test.go @@ -93,7 +93,7 @@ func (s *commonServicesInternalAuthDockerComposeUpTest) TestDockerComposeUp() { url: "http://127.0.0.1:8000/health", responsePayload: `{"status":{"teams":"available"}}`, httpResponseCode: 200, - log: "Starting worker", + log: "motd.py", }, { name: "teams-app", @@ -130,7 +130,7 @@ func (s *commonServicesInternalAuthDockerComposeUpTest) TestDockerComposeUp() { url: "http://127.0.0.1:8000/health", responsePayload: `{"status":{"teams":"available"}}`, httpResponseCode: 200, - log: "Starting worker", + log: "motd.py", }, { name: "teams-app", @@ -167,7 +167,7 @@ func (s *commonServicesInternalAuthDockerComposeUpTest) TestDockerComposeUp() { url: "http://127.0.0.1:8000/health", responsePayload: `{"status":{"teams":"available"}}`, httpResponseCode: 200, - log: "Starting worker", + log: "motd.py", }, { name: "teams-app", @@ -211,7 +211,7 @@ func (s *commonServicesInternalAuthDockerComposeUpTest) TestDockerComposeUp() { url: "http://127.0.0.1:8000/health", responsePayload: `{"status":{"teams":"available"}}`, httpResponseCode: 200, - log: "Starting worker", + log: "motd.py", }, { name: "teams-app", @@ -240,7 +240,7 @@ func (s *commonServicesInternalAuthDockerComposeUpTest) TestDockerComposeUp() { url: "", responsePayload: "", httpResponseCode: 0, - log: "Executor started", + log: "Registering executor builtin", }, }, }, @@ -321,8 +321,10 @@ func (s *commonServicesInternalAuthDockerComposeUpTest) TestDockerComposeUp() { for _, expected := range testCase.expected { logger.Log(subT, fmt.Sprintf("Validating service %s...", expected.name)) - // Validate that docker compose started the container - s.Contains(output, fmt.Sprintf("Container %s-%s-1 Started", dockerOptions.ProjectName, expected.name), fmt.Sprintf("%s - %s - docker compose output should contain service container started", testCase.name, expected.name)) + // Validate that docker compose started the container. + // Compose CLI v2.x emitted two spaces between `-1` and + // `Started`; v5.x emits one. Match either with \s+. + s.Regexp(fmt.Sprintf(`Container %s-%s-1\s+Started`, dockerOptions.ProjectName, expected.name), output, fmt.Sprintf("%s - %s - docker compose output should contain service container started", testCase.name, expected.name)) // Validate endpoint response // Skip fiftyone-app and teams-plugins because they do not have callable endpoints that return a response payload. diff --git a/tests/integration/compose/docker-compose-legacy-auth_test.go b/tests/integration/compose/docker-compose-legacy-auth_test.go index 21dc71f2..3314c64e 100644 --- a/tests/integration/compose/docker-compose-legacy-auth_test.go +++ b/tests/integration/compose/docker-compose-legacy-auth_test.go @@ -93,7 +93,7 @@ func (s *commonServicesLegacyAuthDockerComposeUpTest) TestDockerComposeUp() { url: "http://127.0.0.1:8000/health", responsePayload: `{"status":{"teams":"available"}}`, httpResponseCode: 200, - log: "Starting worker", + log: "motd.py", }, { name: "teams-app", @@ -130,7 +130,7 @@ func (s *commonServicesLegacyAuthDockerComposeUpTest) TestDockerComposeUp() { url: "http://127.0.0.1:8000/health", responsePayload: `{"status":{"teams":"available"}}`, httpResponseCode: 200, - log: "Starting worker", + log: "motd.py", }, { name: "teams-app", @@ -167,7 +167,7 @@ func (s *commonServicesLegacyAuthDockerComposeUpTest) TestDockerComposeUp() { url: "http://127.0.0.1:8000/health", responsePayload: `{"status":{"teams":"available"}}`, httpResponseCode: 200, - log: "Starting worker", + log: "motd.py", }, { name: "teams-app", @@ -211,7 +211,7 @@ func (s *commonServicesLegacyAuthDockerComposeUpTest) TestDockerComposeUp() { url: "http://127.0.0.1:8000/health", responsePayload: `{"status":{"teams":"available"}}`, httpResponseCode: 200, - log: "Starting worker", + log: "motd.py", }, { name: "teams-app", @@ -240,7 +240,7 @@ func (s *commonServicesLegacyAuthDockerComposeUpTest) TestDockerComposeUp() { url: "", responsePayload: "", httpResponseCode: 0, - log: "Executor started", + log: "Registering executor builtin", }, }, }, @@ -320,7 +320,9 @@ func (s *commonServicesLegacyAuthDockerComposeUpTest) TestDockerComposeUp() { // Validate system health for _, expected := range testCase.expected { logger.Log(subT, fmt.Sprintf("Validating service %s...", expected.name)) - s.Contains(output, fmt.Sprintf("Container %s-%s-1 Started", dockerOptions.ProjectName, expected.name), fmt.Sprintf("%s - %s - docker compose output should contain service container started", testCase.name, expected.name)) + // Compose CLI v2.x emitted two spaces between `-1` and + // `Started`; v5.x emits one. Match either with \s+. + s.Regexp(fmt.Sprintf(`Container %s-%s-1\s+Started`, dockerOptions.ProjectName, expected.name), output, fmt.Sprintf("%s - %s - docker compose output should contain service container started", testCase.name, expected.name)) // Validate endpoint response // Skip fiftyone-app and teams-plugins because they do not have callable endpoints that return a response payload. diff --git a/tests/unit/compose/common_test.go b/tests/unit/compose/common_test.go index 229dc064..c78db0c2 100644 --- a/tests/unit/compose/common_test.go +++ b/tests/unit/compose/common_test.go @@ -3,6 +3,14 @@ package unit +import "github.com/compose-spec/compose-go/v2/types" + const ( envFixtureFilePath = "../../fixtures/docker/.env" ) + +var telemetrySocketDriverOpts = types.Options{ + "device": "tmpfs", + "o": "uid=1000,gid=1000,mode=0755", + "type": "tmpfs", +} diff --git a/tests/unit/compose/docker-compose-internal-auth_test.go b/tests/unit/compose/docker-compose-internal-auth_test.go index f96cccd5..f7d1be42 100644 --- a/tests/unit/compose/docker-compose-internal-auth_test.go +++ b/tests/unit/compose/docker-compose-internal-auth_test.go @@ -28,6 +28,7 @@ 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 internalAuthEnvTemplateFilePath = filepath.Join(dockerInternalAuthDir, "env.template") type commonServicesInternalAuthDockerComposeTest struct { @@ -59,48 +60,94 @@ 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", + "fiftyone-app-telemetry", "teams-api", + "teams-api-telemetry", "teams-app", "teams-cas", + "telemetry-redis", }, }, { "composePlugins", []string{internalAuthComposePluginsFile}, s.dotEnvFiles, + nil, []string{ "fiftyone-app", + "fiftyone-app-telemetry", "teams-api", + "teams-api-telemetry", "teams-app", "teams-cas", + "telemetry-redis", }, }, { "composeDedicatedPlugins", []string{internalAuthComposeDedicatedPluginsFile}, 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", }, }, { "composeDelegatedOperations", - []string{internalAuthComposeDelegatedOperationsFile}, + []string{internalAuthComposeFile, internalAuthComposeDelegatedOperationsFile}, + s.dotEnvFiles, + nil, + []string{ + "fiftyone-app", + "fiftyone-app-telemetry", + "teams-api", + "teams-api-telemetry", + "teams-app", + "teams-cas", + "teams-do", + "teams-do-telemetry", + "telemetry-redis", + }, + }, + { + "composeDelegatedOperationsGpu", + []string{ + internalAuthComposeFile, + internalAuthComposeDelegatedOperationsFile, + internalAuthComposeDelegatedOperationsGpuFile, + }, 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", + "teams-do-telemetry", + "telemetry-redis", }, }, } @@ -118,6 +165,7 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServicesNames() { cli.WithName(s.projectName), cli.WithEnvFiles(testCase.envFiles...), cli.WithDotEnv, + cli.WithProfiles(testCase.profiles), ) s.NoError(err) @@ -183,10 +231,48 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceImage() { { "delegatedOperationsTeamsDo", "teams-do", - []string{internalAuthComposeDelegatedOperationsFile}, + []string{internalAuthComposeFile, internalAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, "voxel51/fiftyone-teams-cv-full:v2.19.0", }, + { + "telemetryRedis", + "telemetry-redis", + []string{internalAuthComposeFile}, + s.dotEnvFiles, + "redis:7-alpine", + }, + { + "telemetrySidecarFiftyoneApp", + "fiftyone-app-telemetry", + []string{internalAuthComposeFile}, + s.dotEnvFiles, + "voxel51/telemetry-sidecar:v2.19.0", + }, + { + "telemetrySidecarTeamsApi", + "teams-api-telemetry", + []string{internalAuthComposeFile}, + s.dotEnvFiles, + "voxel51/telemetry-sidecar:v2.19.0", + }, + { + "telemetrySidecarTeamsDo", + "teams-do-telemetry", + []string{ + internalAuthComposeFile, + internalAuthComposeDelegatedOperationsFile, + }, + s.dotEnvFiles, + "voxel51/telemetry-sidecar:v2.19.0", + }, + { + "telemetrySidecarTeamsPlugins", + "teams-plugins-telemetry", + []string{internalAuthComposeDedicatedPluginsFile}, + s.dotEnvFiles, + "voxel51/telemetry-sidecar:v2.19.0", + }, } for _, testCase := range testCases { @@ -250,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", }, }, { @@ -267,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", @@ -287,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", @@ -334,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", }, }, { @@ -351,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", @@ -372,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", @@ -418,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", }, }, { @@ -435,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", @@ -456,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", @@ -501,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", @@ -517,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", }, }, } @@ -707,7 +805,7 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceRestart() { { "delegatedOperationsTeamsDo", "teams-do", - []string{internalAuthComposeDelegatedOperationsFile}, + []string{internalAuthComposeFile, internalAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, types.RestartPolicyAlways, }, @@ -905,7 +1003,7 @@ func (s *commonServicesInternalAuthDockerComposeTest) TestServiceVolumes() { { "delegatedOperationsTeamsDo", "teams-do", - []string{internalAuthComposeDelegatedOperationsFile}, + []string{internalAuthComposeFile, internalAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, []types.ServiceVolumeConfig{ { @@ -915,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{}, + }, }, }, } @@ -962,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", @@ -972,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", + }, }, }, { @@ -982,16 +1094,38 @@ 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", + DriverOpts: telemetrySocketDriverOpts, + }, + // telemetry-socket-{2,3} are declared at the project level + // so they appear in project.Volumes regardless of which + // `do-N` profile is active. Slot 2/3 services only start + // when the matching profile is selected. + "telemetry-socket-2": { + Name: "fiftyone-compose-test_telemetry-socket-2", + DriverOpts: telemetrySocketDriverOpts, + }, + "telemetry-socket-3": { + Name: "fiftyone-compose-test_telemetry-socket-3", + DriverOpts: telemetrySocketDriverOpts, + }, }, }, } diff --git a/tests/unit/compose/docker-compose-legacy-auth_test.go b/tests/unit/compose/docker-compose-legacy-auth_test.go index 1a48c5c4..0875add5 100644 --- a/tests/unit/compose/docker-compose-legacy-auth_test.go +++ b/tests/unit/compose/docker-compose-legacy-auth_test.go @@ -28,6 +28,7 @@ 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 legacyAuthEnvTemplateFilePath = filepath.Join(dockerLegacyAuthDir, "env.template") type commonServicesLegacyAuthDockerComposeTest struct { @@ -59,48 +60,94 @@ 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", + "fiftyone-app-telemetry", "teams-api", + "teams-api-telemetry", "teams-app", "teams-cas", + "telemetry-redis", }, }, { "composePlugins", []string{legacyAuthComposePluginsFile}, s.dotEnvFiles, + nil, []string{ "fiftyone-app", + "fiftyone-app-telemetry", "teams-api", + "teams-api-telemetry", "teams-app", "teams-cas", + "telemetry-redis", }, }, { "composeDedicatedPlugins", []string{legacyAuthComposeDedicatedPluginsFile}, 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", }, }, { "composeDelegatedOperations", - []string{legacyAuthComposeDelegatedOperationsFile}, + []string{legacyAuthComposeFile, legacyAuthComposeDelegatedOperationsFile}, + s.dotEnvFiles, + nil, + []string{ + "fiftyone-app", + "fiftyone-app-telemetry", + "teams-api", + "teams-api-telemetry", + "teams-app", + "teams-cas", + "teams-do", + "teams-do-telemetry", + "telemetry-redis", + }, + }, + { + "composeDelegatedOperationsGpu", + []string{ + legacyAuthComposeFile, + legacyAuthComposeDelegatedOperationsFile, + legacyAuthComposeDelegatedOperationsGpuFile, + }, 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", + "teams-do-telemetry", + "telemetry-redis", }, }, } @@ -118,6 +165,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServicesNames() { cli.WithName(s.projectName), cli.WithEnvFiles(testCase.envFiles...), cli.WithDotEnv, + cli.WithProfiles(testCase.profiles), ) s.NoError(err) @@ -183,10 +231,48 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceImage() { { "delegatedOperationsTeamsDo", "teams-do", - []string{legacyAuthComposeDelegatedOperationsFile}, + []string{legacyAuthComposeFile, legacyAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, "voxel51/fiftyone-teams-cv-full:v2.19.0", }, + { + "telemetryRedis", + "telemetry-redis", + []string{legacyAuthComposeFile}, + s.dotEnvFiles, + "redis:7-alpine", + }, + { + "telemetrySidecarFiftyoneApp", + "fiftyone-app-telemetry", + []string{legacyAuthComposeFile}, + s.dotEnvFiles, + "voxel51/telemetry-sidecar:v2.19.0", + }, + { + "telemetrySidecarTeamsApi", + "teams-api-telemetry", + []string{legacyAuthComposeFile}, + s.dotEnvFiles, + "voxel51/telemetry-sidecar:v2.19.0", + }, + { + "telemetrySidecarTeamsDo", + "teams-do-telemetry", + []string{ + legacyAuthComposeFile, + legacyAuthComposeDelegatedOperationsFile, + }, + s.dotEnvFiles, + "voxel51/telemetry-sidecar:v2.19.0", + }, + { + "telemetrySidecarTeamsPlugins", + "teams-plugins-telemetry", + []string{legacyAuthComposeDedicatedPluginsFile}, + s.dotEnvFiles, + "voxel51/telemetry-sidecar:v2.19.0", + }, } for _, testCase := range testCases { @@ -250,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", }, }, { @@ -267,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", @@ -287,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", @@ -332,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", }, }, { @@ -349,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", @@ -369,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", @@ -415,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", }, }, { @@ -433,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", @@ -453,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", @@ -498,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", }, }, { @@ -516,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", @@ -536,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", @@ -580,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", @@ -596,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", }, }, } @@ -791,7 +892,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceRestart() { { "delegatedOperationsTeamsDo", "teams-do", - []string{legacyAuthComposeDelegatedOperationsFile}, + []string{legacyAuthComposeFile, legacyAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, types.RestartPolicyAlways, }, @@ -989,7 +1090,7 @@ func (s *commonServicesLegacyAuthDockerComposeTest) TestServiceVolumes() { { "delegatedOperationsTeamsDo", "teams-do", - []string{legacyAuthComposeDelegatedOperationsFile}, + []string{legacyAuthComposeFile, legacyAuthComposeDelegatedOperationsFile}, s.dotEnvFiles, []types.ServiceVolumeConfig{ { @@ -999,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{}, + }, }, }, } @@ -1046,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", @@ -1056,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", + }, }, }, { @@ -1066,16 +1181,38 @@ 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", + DriverOpts: telemetrySocketDriverOpts, + }, + // telemetry-socket-{2,3} are declared at the project level + // so they appear in project.Volumes regardless of which + // `do-N` profile is active. Slot 2/3 services only start + // when the matching profile is selected. + "telemetry-socket-2": { + Name: "fiftyone-compose-test_telemetry-socket-2", + DriverOpts: telemetrySocketDriverOpts, + }, + "telemetry-socket-3": { + Name: "fiftyone-compose-test_telemetry-socket-3", + DriverOpts: telemetrySocketDriverOpts, + }, }, }, } diff --git a/tests/unit/helm/api-deployment_test.go b/tests/unit/helm/api-deployment_test.go index 30c37c93..b4231b99 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 @@ -2395,11 +2400,11 @@ func (s *deploymentApiTemplateTest) TestPodSecurityContext() { "defaultValues", nil, func(podSecurityContext *corev1.PodSecurityContext) { - s.Nil(podSecurityContext.FSGroup, "should be nil") + s.Equal(int64(1000), *podSecurityContext.FSGroup, "fsGroup should be 1000 (image UID)") + s.Equal(int64(1000), *podSecurityContext.RunAsGroup, "runAsGroup should be 1000") + s.True(*podSecurityContext.RunAsNonRoot, "runAsNonRoot should be true") + s.Equal(int64(1000), *podSecurityContext.RunAsUser, "runAsUser should be 1000") s.Nil(podSecurityContext.FSGroupChangePolicy, "should be nil") - s.Nil(podSecurityContext.RunAsGroup, "should be nil") - s.Nil(podSecurityContext.RunAsNonRoot, "should be nil") - s.Nil(podSecurityContext.RunAsUser, "should be nil") s.Nil(podSecurityContext.SeccompProfile, "should be nil") s.Nil(podSecurityContext.SELinuxOptions, "should be nil") s.Nil(podSecurityContext.SupplementalGroups, "should be nil") @@ -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/api-role_test.go b/tests/unit/helm/api-role_test.go index 13e1aeb8..71e5ad3f 100644 --- a/tests/unit/helm/api-role_test.go +++ b/tests/unit/helm/api-role_test.go @@ -286,6 +286,28 @@ func (s *apiRoleTemplateTest) TestMetadataLabels() { } func (s *apiRoleTemplateTest) TestRules() { + // The pods/log GET rule must remain present regardless of telemetry + // settings — the api uses it for its own DO log retrieval flows, and + // the telemetry RoleBinding piggybacks on this rule for the api SA + // (see _telemetry.tpl: telemetry.role.subjects). Render both with + // telemetry on and off to lock that in. + expectedRulesJson := `[ + { + "apiGroups": ["batch"], + "resources": ["jobs"], + "verbs": ["create", "get", "list", "watch", "update", "delete"] + }, + { + "apiGroups": [""], + "resources": ["pods"], + "verbs": ["get", "list", "watch", "delete"] + }, + { + "apiGroups": [""], + "resources": ["pods/log"], + "verbs": ["get"] + } + ]` testCases := []struct { name string values map[string]string @@ -295,24 +317,23 @@ func (s *apiRoleTemplateTest) TestRules() { "defaultValues", nil, func(rules []rbacv1.PolicyRule) { - expectedRulesJson := `[ - { - "apiGroups": ["batch"], - "resources": ["jobs"], - "verbs": ["create", "get", "list", "watch", "update", "delete"] - }, - { - "apiGroups": [""], - "resources": ["pods"], - "verbs": ["get", "list", "watch", "delete"] - } - ]` var expectedRules []rbacv1.PolicyRule err := json.Unmarshal([]byte(expectedRulesJson), &expectedRules) s.NoError(err) s.Equal(expectedRules, rules, "Rules should be equal") }, }, + { + "telemetryDisabled", + map[string]string{"telemetry.enabled": "false"}, + func(rules []rbacv1.PolicyRule) { + var expectedRules []rbacv1.PolicyRule + err := json.Unmarshal([]byte(expectedRulesJson), &expectedRules) + s.NoError(err) + s.Equal(expectedRules, rules, + "api-role rules must be unchanged when telemetry is disabled") + }, + }, } for _, testCase := range testCases { diff --git a/tests/unit/helm/app-deployment_test.go b/tests/unit/helm/app-deployment_test.go index bfb1b929..5637978c 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 @@ -2091,11 +2091,11 @@ func (s *deploymentAppTemplateTest) TestPodSecurityContext() { "defaultValues", nil, func(podSecurityContext *corev1.PodSecurityContext) { - s.Nil(podSecurityContext.FSGroup, "should be nil") + s.Equal(int64(1000), *podSecurityContext.FSGroup, "fsGroup should be 1000 (image UID)") + s.Equal(int64(1000), *podSecurityContext.RunAsGroup, "runAsGroup should be 1000") + s.True(*podSecurityContext.RunAsNonRoot, "runAsNonRoot should be true") + s.Equal(int64(1000), *podSecurityContext.RunAsUser, "runAsUser should be 1000") s.Nil(podSecurityContext.FSGroupChangePolicy, "should be nil") - s.Nil(podSecurityContext.RunAsGroup, "should be nil") - s.Nil(podSecurityContext.RunAsNonRoot, "should be nil") - s.Nil(podSecurityContext.RunAsUser, "should be nil") s.Nil(podSecurityContext.SeccompProfile, "should be nil") s.Nil(podSecurityContext.SELinuxOptions, "should be nil") s.Nil(podSecurityContext.SupplementalGroups, "should be nil") @@ -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/common_test.go b/tests/unit/helm/common_test.go index 2ce0909b..80b0d34d 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 || unitTelemetrySidecar +// +build all helm unit unitApiDeployment unitApiService unitAppDeployment unitAppHpa unitAppService unitCasDeployment unitCasService unitIngress unitNamespace unitPluginsDeployment unitHpaPlugins unitPluginsService unitSecrets unitServiceAccount unitTeamsAppDeployment unitTeamsAppHpa unitTeamsAppService unitTelemetryRedis unitTelemetryRoleBinding unitTelemetrySidecar package unit diff --git a/tests/unit/helm/delegated-operator-instance-deployment_test.go b/tests/unit/helm/delegated-operator-instance-deployment_test.go index ba744ea5..a33b7f6b 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, "---") @@ -3652,11 +3652,11 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestPodSecurityContext []func(podSecurityContext *corev1.PodSecurityContext){ func(podSecurityContext *corev1.PodSecurityContext) { - s.Nil(podSecurityContext.FSGroup, "should be nil") + s.Equal(int64(1000), *podSecurityContext.FSGroup, "fsGroup should be 1000 (image UID)") + s.Equal(int64(1000), *podSecurityContext.RunAsGroup, "runAsGroup should be 1000") + s.True(*podSecurityContext.RunAsNonRoot, "runAsNonRoot should be true") + s.Equal(int64(1000), *podSecurityContext.RunAsUser, "runAsUser should be 1000") s.Nil(podSecurityContext.FSGroupChangePolicy, "should be nil") - s.Nil(podSecurityContext.RunAsGroup, "should be nil") - s.Nil(podSecurityContext.RunAsNonRoot, "should be nil") - s.Nil(podSecurityContext.RunAsUser, "should be nil") s.Nil(podSecurityContext.SeccompProfile, "should be nil") s.Nil(podSecurityContext.SELinuxOptions, "should be nil") s.Nil(podSecurityContext.SupplementalGroups, "should be nil") @@ -3672,11 +3672,11 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestPodSecurityContext }, []func(podSecurityContext *corev1.PodSecurityContext){ func(podSecurityContext *corev1.PodSecurityContext) { - s.Nil(podSecurityContext.FSGroup, "should be nil") + s.Equal(int64(1000), *podSecurityContext.FSGroup, "fsGroup should be 1000 (image UID)") + s.Equal(int64(1000), *podSecurityContext.RunAsGroup, "runAsGroup should be 1000") + s.True(*podSecurityContext.RunAsNonRoot, "runAsNonRoot should be true") + s.Equal(int64(1000), *podSecurityContext.RunAsUser, "runAsUser should be 1000") s.Nil(podSecurityContext.FSGroupChangePolicy, "should be nil") - s.Nil(podSecurityContext.RunAsGroup, "should be nil") - s.Nil(podSecurityContext.RunAsNonRoot, "should be nil") - s.Nil(podSecurityContext.RunAsUser, "should be nil") s.Nil(podSecurityContext.SeccompProfile, "should be nil") s.Nil(podSecurityContext.SELinuxOptions, "should be nil") s.Nil(podSecurityContext.SupplementalGroups, "should be nil") @@ -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 { @@ -5689,3 +5689,97 @@ func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestDeploymentUpdateSt }) } } + +func (s *deploymentDelegatedOperatorInstanceTemplateTest) TestTelemetrySocketInjection() { + const socketName = "telemetry-socket" + + countVolumes := func(vols []corev1.Volume, name string) int { + n := 0 + for _, v := range vols { + if v.Name == name { + n++ + } + } + return n + } + countMounts := func(mounts []corev1.VolumeMount, name string) int { + n := 0 + for _, m := range mounts { + if m.Name == name { + n++ + } + } + return n + } + // findContainer returns the named container (the main DO container, + // not the telemetry-sidecar) from a pod spec. + findContainer := func(containers []corev1.Container, name string) *corev1.Container { + for i, c := range containers { + if c.Name == name { + return &containers[i] + } + } + return nil + } + + testCases := []struct { + name string + values map[string]string + expectVol int + expectMnt int + }{ + { + name: "autoInjectsWhenUserHasNoTelemetrySocket", + values: map[string]string{ + "telemetry.enabled": "true", + "delegatedOperatorDeployments.deployments.teamsDoCpuDefault.enabled": "true", + }, + expectVol: 1, + expectMnt: 1, + }, + { + name: "doesNotDuplicateWhenUserDeclaresTelemetrySocket", + values: map[string]string{ + "telemetry.enabled": "true", + "delegatedOperatorDeployments.deployments.teamsDoCpuDefault.enabled": "true", + "delegatedOperatorDeployments.deployments.teamsDoCpuDefault.volumes[0].name": socketName, + "delegatedOperatorDeployments.deployments.teamsDoCpuDefault.volumes[0].emptyDir.medium": "Memory", + "delegatedOperatorDeployments.deployments.teamsDoCpuDefault.volumeMounts[0].name": socketName, + "delegatedOperatorDeployments.deployments.teamsDoCpuDefault.volumeMounts[0].mountPath": "/custom/path", + }, + expectVol: 1, + expectMnt: 1, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + subT := s.T() + subT.Parallel() + + options := &helm.Options{SetValues: tc.values} + output := helm.RenderTemplate(subT, options, s.chartPath, s.releaseName, s.templates) + + docs := strings.Split(output, "---") + s.Require().GreaterOrEqual(len(docs), 2, "expected at least one rendered deployment") + + var deployment appsv1.Deployment + helm.UnmarshalK8SYaml(subT, docs[1], &deployment) + + s.Equal( + tc.expectVol, + countVolumes(deployment.Spec.Template.Spec.Volumes, socketName), + "telemetry-socket volume count mismatch", + ) + + main := findContainer(deployment.Spec.Template.Spec.Containers, "teams-do-cpu-default") + s.Require().NotNil(main, "main DO container not found") + s.Equal( + tc.expectMnt, + countMounts(main.VolumeMounts, socketName), + "telemetry-socket volumeMount count mismatch on main container", + ) + }) + } +} diff --git a/tests/unit/helm/delegated-operator-job-configmap_test.go b/tests/unit/helm/delegated-operator-job-configmap_test.go index 9ab313a4..f036a884 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) @@ -650,6 +650,110 @@ func (s *doK8sConfigMapTemplateTest) TestData() { } } +// renderJob renders a single job from the rendered ConfigMap data and +// returns it as a batchv1.Job for introspection. The job template body +// in the ConfigMap is Jinja, which we render with placeholder values so +// the result is valid YAML. +func (s *doK8sConfigMapTemplateTest) renderJob(data map[string]string, key string) batchv1.Job { + s.T().Helper() + s.Require().Contains(data, key, "ConfigMap should contain key %q", key) + + jinjaArgs := map[string]interface{}{ + "_id": "test-id", + "_command": "fiftyone", + "_args": []string{"test"}, + } + jobYAML, err := convertJinjaToYAML(data[key], jinjaArgs) + s.Require().NoError(err) + + var job batchv1.Job + helm.UnmarshalK8SYaml(s.T(), jobYAML, &job) + return job +} + +func (s *doK8sConfigMapTemplateTest) TestTelemetrySocketInjection() { + const socketName = "telemetry-socket" + jobKey := "cpuDefault.yaml" + + // helper: count entries with the given name + countByName := func(volumes []corev1.Volume, name string) int { + n := 0 + for _, v := range volumes { + if v.Name == name { + n++ + } + } + return n + } + countMountsByName := func(mounts []corev1.VolumeMount, name string) int { + n := 0 + for _, m := range mounts { + if m.Name == name { + n++ + } + } + return n + } + + testCases := []struct { + name string + values map[string]string + expectVol int // expected occurrences of telemetry-socket volume + expectMnt int // expected occurrences of telemetry-socket mount + }{ + { + name: "autoInjectsWhenUserHasNoTelemetrySocket", + values: map[string]string{ + "telemetry.enabled": "true", + "delegatedOperatorJobTemplates.jobs.cpuDefault.unused": "nil", + }, + expectVol: 1, + expectMnt: 1, + }, + { + name: "doesNotDuplicateWhenUserDeclaresTelemetrySocket", + values: map[string]string{ + "telemetry.enabled": "true", + "delegatedOperatorJobTemplates.jobs.cpuDefault.unused": "nil", + "delegatedOperatorJobTemplates.jobs.cpuDefault.volumes[0].name": socketName, + "delegatedOperatorJobTemplates.jobs.cpuDefault.volumes[0].emptyDir.medium": "Memory", + "delegatedOperatorJobTemplates.jobs.cpuDefault.volumeMounts[0].name": socketName, + "delegatedOperatorJobTemplates.jobs.cpuDefault.volumeMounts[0].mountPath": "/custom/path", + }, + expectVol: 1, + expectMnt: 1, + }, + } + + 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) + + job := s.renderJob(configMap.Data, jobKey) + + s.Equal( + testCase.expectVol, + countByName(job.Spec.Template.Spec.Volumes, socketName), + "telemetry-socket volume count mismatch", + ) + s.Require().NotEmpty(job.Spec.Template.Spec.Containers, "expected at least one container") + s.Equal( + testCase.expectMnt, + countMountsByName(job.Spec.Template.Spec.Containers[0].VolumeMounts, socketName), + "telemetry-socket volumeMount count mismatch", + ) + }) + } +} + func (s *doK8sConfigMapTemplateTest) loadTestFile(filename, chartVersion string) string { content, err := os.ReadFile(filename) s.NoError(err, "Failed to read test file: %s", filename) diff --git a/tests/unit/helm/plugins-deployment_test.go b/tests/unit/helm/plugins-deployment_test.go index 72ba8940..f0009eeb 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 @@ -2522,11 +2522,11 @@ func (s *deploymentPluginsTemplateTest) TestPodSecurityContext() { "pluginsSettings.enabled": "true", }, func(podSecurityContext *corev1.PodSecurityContext) { - s.Nil(podSecurityContext.FSGroup, "should be nil") + s.Equal(int64(1000), *podSecurityContext.FSGroup, "fsGroup should be 1000 (image UID)") + s.Equal(int64(1000), *podSecurityContext.RunAsGroup, "runAsGroup should be 1000") + s.True(*podSecurityContext.RunAsNonRoot, "runAsNonRoot should be true") + s.Equal(int64(1000), *podSecurityContext.RunAsUser, "runAsUser should be 1000") s.Nil(podSecurityContext.FSGroupChangePolicy, "should be nil") - s.Nil(podSecurityContext.RunAsGroup, "should be nil") - s.Nil(podSecurityContext.RunAsNonRoot, "should be nil") - s.Nil(podSecurityContext.RunAsUser, "should be nil") s.Nil(podSecurityContext.SeccompProfile, "should be nil") s.Nil(podSecurityContext.SELinuxOptions, "should be nil") s.Nil(podSecurityContext.SupplementalGroups, "should be nil") @@ -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 9a51fad5..e1c96eff 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-redis_test.go b/tests/unit/helm/telemetry-redis_test.go new file mode 100644 index 00000000..fb18bfb1 --- /dev/null +++ b/tests/unit/helm/telemetry-redis_test.go @@ -0,0 +1,412 @@ +//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 + deploymentTpl string + pvcTpl string + serviceTpl string + allBundledTpls []string +} + +func TestTelemetryRedisTemplate(t *testing.T) { + t.Parallel() + + helmChartPath, err := filepath.Abs(chartPath) + require.NoError(t, err) + + deploymentTpl := "templates/telemetry-redis-deployment.yaml" + pvcTpl := "templates/telemetry-redis-pvc.yaml" + serviceTpl := "templates/telemetry-redis-service.yaml" + + suite.Run(t, &telemetryRedisTemplateTest{ + Suite: suite.Suite{}, + chartPath: helmChartPath, + releaseName: "fiftyone-test", + namespace: "fiftyone-" + strings.ToLower(random.UniqueId()), + deploymentTpl: deploymentTpl, + pvcTpl: pvcTpl, + serviceTpl: serviceTpl, + allBundledTpls: []string{deploymentTpl, pvcTpl, serviceTpl}, + }) +} + +func (s *telemetryRedisTemplateTest) TestEnabledByDefault() { + options := &helm.Options{SetValues: nil} + + // Deployment + Service render unconditionally when telemetry is enabled. + deploymentOut, err := helm.RenderTemplateE(s.T(), options, s.chartPath, s.releaseName, []string{s.deploymentTpl}) + s.Require().NoError(err) + s.Contains(deploymentOut, "kind: Deployment", "Redis Deployment should be rendered by default") + + serviceOut, err := helm.RenderTemplateE(s.T(), options, s.chartPath, s.releaseName, []string{s.serviceTpl}) + s.Require().NoError(err) + s.Contains(serviceOut, "kind: Service", "Redis Service should be rendered by default") + + // persistence.enabled defaults to false → PVC template renders empty so the + // chart installs cleanly on clusters without a default StorageClass. + _, err = helm.RenderTemplateE(s.T(), options, s.chartPath, s.releaseName, []string{s.pvcTpl}) + s.ErrorContains(err, "could not find template", + "Redis PVC should NOT 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.allBundledTpls) + s.ErrorContains(err, "could not find template") +} + +// TestExternalUrlSkipsBundled ensures that setting telemetry.redis.external.url +// causes the bundled Redis Deployment/Service/PVC to NOT be rendered. The +// chart should leave Redis provisioning to the operator in this case. +func (s *telemetryRedisTemplateTest) TestExternalUrlSkipsBundled() { + options := &helm.Options{SetValues: map[string]string{ + "telemetry.redis.external.url": "redis://my-managed-redis:6379", + }} + + _, err := helm.RenderTemplateE(s.T(), options, s.chartPath, s.releaseName, s.allBundledTpls) + s.ErrorContains(err, "could not find template") +} + +// TestExternalUrlWiresApiDeployment ensures that setting external.url causes +// FIFTYONE_TELEMETRY_REDIS_URL on the api deployment to point at the external +// URL rather than the in-cluster Service. +func (s *telemetryRedisTemplateTest) TestExternalUrlWiresApiDeployment() { + options := &helm.Options{SetValues: map[string]string{ + "telemetry.redis.external.url": "redis://my-managed-redis:6379", + }} + + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, + []string{"templates/api-deployment.yaml"}) + + var deployment appsv1.Deployment + helm.UnmarshalK8SYaml(s.T(), output, &deployment) + s.Require().Len(deployment.Spec.Template.Spec.Containers, 2, + "Expected api + telemetry-sidecar containers") + + for _, container := range deployment.Spec.Template.Spec.Containers { + var found *corev1.EnvVar + for i, ev := range container.Env { + if ev.Name == "FIFTYONE_TELEMETRY_REDIS_URL" { + found = &container.Env[i] + break + } + } + s.Require().NotNil(found, + "FIFTYONE_TELEMETRY_REDIS_URL should be set on %s container", container.Name) + s.Equal("redis://my-managed-redis:6379", found.Value, + "FIFTYONE_TELEMETRY_REDIS_URL on %s should point at the external URL", + container.Name) + } +} + +// TestBundledUrlWiresApiDeployment ensures the default in-cluster URL is +// release-scoped on the api deployment's containers. +func (s *telemetryRedisTemplateTest) TestBundledUrlWiresApiDeployment() { + options := &helm.Options{SetValues: nil} + + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, + []string{"templates/api-deployment.yaml"}) + + var deployment appsv1.Deployment + helm.UnmarshalK8SYaml(s.T(), output, &deployment) + + expectedURL := fmt.Sprintf("redis://%s-telemetry-redis.%s.svc.cluster.local:6379", + s.releaseName, "fiftyone-teams") + for _, container := range deployment.Spec.Template.Spec.Containers { + for _, ev := range container.Env { + if ev.Name == "FIFTYONE_TELEMETRY_REDIS_URL" { + s.Equal(expectedURL, ev.Value, + "FIFTYONE_TELEMETRY_REDIS_URL on %s should be release-scoped in-cluster URL", + container.Name) + } + } + } +} + +// TestExternalUrlWiresDelegatedOperatorDeployment regression-tests that the +// delegated-operator deployment's workload-container env honors external.url. +// Earlier the DO env-vars helper built the URL inline via `printf "redis://..."` +// rather than going through the `telemetry.redis.url` helper, so it always +// pointed at the in-cluster Service even when external.url was set. +func (s *telemetryRedisTemplateTest) TestExternalUrlWiresDelegatedOperatorDeployment() { + options := &helm.Options{SetValues: map[string]string{ + "telemetry.redis.external.url": "redis://my-managed-redis:6379", + "delegatedOperatorDeployments.deployments.teamsDoCpuDefault.enabled": "true", + }} + + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, + []string{"templates/delegated-operator-instance-deployment.yaml"}) + + var deployment appsv1.Deployment + helm.UnmarshalK8SYaml(s.T(), output, &deployment) + + // Both the workload container and the auto-injected sidecar should see + // the external URL. + s.Require().GreaterOrEqual(len(deployment.Spec.Template.Spec.Containers), 1, + "Expected at least one container in DO deployment") + for _, container := range deployment.Spec.Template.Spec.Containers { + var found *corev1.EnvVar + for i, ev := range container.Env { + if ev.Name == "FIFTYONE_TELEMETRY_REDIS_URL" { + found = &container.Env[i] + break + } + } + s.Require().NotNil(found, + "FIFTYONE_TELEMETRY_REDIS_URL should be set on %s container", container.Name) + s.Equal("redis://my-managed-redis:6379", found.Value, + "FIFTYONE_TELEMETRY_REDIS_URL on %s should point at the external URL", + container.Name) + } +} + +// TestExternalUrlWiresDelegatedOperatorJobConfigMap regression-tests that the +// DO Job template (rendered into the do-templates ConfigMap) honors +// external.url. Same root cause as the DO deployment bug above — the +// templates env-vars helper built the URL inline rather than going through +// `telemetry.redis.url`. +func (s *telemetryRedisTemplateTest) TestExternalUrlWiresDelegatedOperatorJobConfigMap() { + options := &helm.Options{SetValues: map[string]string{ + "telemetry.redis.external.url": "redis://my-managed-redis:6379", + "delegatedOperatorJobTemplates.jobs.test-job.enabled": "true", + }} + + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, + []string{"templates/delegated-operator-job-configmap.yaml"}) + + // The ConfigMap embeds the Job spec as a multi-line YAML string in + // .data.. The render output is plain text, so we just need to + // ensure no occurrence of the in-cluster URL leaks through. + inClusterURL := fmt.Sprintf("redis://%s-telemetry-redis.%s.svc.cluster.local:6379", + s.releaseName, "fiftyone-teams") + s.NotContains(output, inClusterURL, + "DO Job ConfigMap must not embed the in-cluster Redis URL when external.url is set") + s.Contains(output, "redis://my-managed-redis:6379", + "DO Job ConfigMap should embed the external Redis URL") +} + +// renderDeployment renders just the Deployment template and unmarshals it. +func (s *telemetryRedisTemplateTest) renderDeployment(options *helm.Options) appsv1.Deployment { + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, []string{s.deploymentTpl}) + var deployment appsv1.Deployment + helm.UnmarshalK8SYaml(s.T(), output, &deployment) + return deployment +} + +func (s *telemetryRedisTemplateTest) TestDeploymentMetadata() { + options := &helm.Options{SetValues: map[string]string{ + "telemetry.enabled": "true", + }} + + deployment := s.renderDeployment(options) + + 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", + }} + + deployment := s.renderDeployment(options) + s.Equal("my-ns", deployment.ObjectMeta.Namespace) +} + +func (s *telemetryRedisTemplateTest) TestDeploymentDefaultImage() { + options := &helm.Options{SetValues: map[string]string{ + "telemetry.enabled": "true", + }} + + deployment := s.renderDeployment(options) + + 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", + }} + + deployment := s.renderDeployment(options) + 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", + }} + + deployment := s.renderDeployment(options) + + 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, "allkeys-lru", "maxmemory-policy should be hardcoded to allkeys-lru") +} + +func (s *telemetryRedisTemplateTest) TestServiceMetadata() { + options := &helm.Options{SetValues: map[string]string{ + "telemetry.enabled": "true", + }} + + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, []string{s.serviceTpl}) + + var svc corev1.Service + helm.UnmarshalK8SYaml(s.T(), output, &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) +} + +func (s *telemetryRedisTemplateTest) TestPVCMetadata() { + // persistence.enabled defaults to false; re-enable to exercise the PVC-rendering path. + options := &helm.Options{SetValues: map[string]string{ + "telemetry.enabled": "true", + "telemetry.redis.persistence.enabled": "true", + "telemetry.redis.persistence.size": "5Gi", + "telemetry.redis.persistence.storageClass": "gp3", + }} + + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, []string{s.pvcTpl}) + + var pvc corev1.PersistentVolumeClaim + helm.UnmarshalK8SYaml(s.T(), output, &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()) + s.Require().NotNil(pvc.Spec.StorageClassName, "StorageClassName should be set") + s.Equal("gp3", *pvc.Spec.StorageClassName) +} + +// assertPVCNotRendered confirms the PVC template renders empty for the given +// values — i.e. the chart does NOT create a PVC. Helm errors with "could not +// find template" when --show-only targets a template that produces no output. +func (s *telemetryRedisTemplateTest) assertPVCNotRendered(options *helm.Options, msg string) { + _, err := helm.RenderTemplateE(s.T(), options, s.chartPath, s.releaseName, []string{s.pvcTpl}) + s.ErrorContains(err, "could not find template", msg) +} + +// TestPersistenceDisabledSkipsPVCAndUsesEmptyDir ensures that with +// persistence.enabled=false, the PVC is not rendered and the Deployment +// switches the redis-data volume to emptyDir. +func (s *telemetryRedisTemplateTest) TestPersistenceDisabledSkipsPVCAndUsesEmptyDir() { + options := &helm.Options{SetValues: map[string]string{ + "telemetry.redis.persistence.enabled": "false", + }} + + s.assertPVCNotRendered(options, "PVC should not render when persistence.enabled=false") + + deployment := s.renderDeployment(options) + volumes := deployment.Spec.Template.Spec.Volumes + s.Require().Len(volumes, 1, "Deployment should have exactly one volume") + s.Equal("redis-data", volumes[0].Name) + s.NotNil(volumes[0].EmptyDir, + "redis-data volume should be emptyDir when persistence is disabled") + s.Nil(volumes[0].PersistentVolumeClaim, + "redis-data volume should NOT reference a PVC when persistence is disabled") +} + +// TestExistingClaimSkipsPVCAndMountsNamedClaim ensures that +// persistence.existingClaim points the Deployment at the named PVC and +// suppresses chart-managed PVC creation — the path for clusters without +// a dynamic PV provisioner where the operator pre-creates the claim. +func (s *telemetryRedisTemplateTest) TestExistingClaimSkipsPVCAndMountsNamedClaim() { + // persistence.enabled defaults to false; re-enable to exercise the existingClaim mount path. + options := &helm.Options{SetValues: map[string]string{ + "telemetry.redis.persistence.enabled": "true", + "telemetry.redis.persistence.existingClaim": "my-prebuilt-redis-pvc", + }} + + s.assertPVCNotRendered(options, "PVC should not render when persistence.existingClaim is set") + + deployment := s.renderDeployment(options) + volumes := deployment.Spec.Template.Spec.Volumes + s.Require().Len(volumes, 1, "Deployment should have exactly one volume") + s.Equal("redis-data", volumes[0].Name) + s.Require().NotNil(volumes[0].PersistentVolumeClaim, + "redis-data volume should reference a PVC when existingClaim is set") + s.Equal("my-prebuilt-redis-pvc", volumes[0].PersistentVolumeClaim.ClaimName, + "claimName should match the user-supplied existingClaim, not the chart-generated name") + s.Nil(volumes[0].EmptyDir, + "redis-data volume should NOT be emptyDir when existingClaim is set") +} + +// TestExistingClaimIgnoresStorageClassAndSize ensures that size/storageClass +// have no effect once the user has taken over claim provisioning via +// existingClaim — the chart has no PVC to apply them to. +func (s *telemetryRedisTemplateTest) TestExistingClaimIgnoresStorageClassAndSize() { + // persistence.enabled defaults to false; re-enable so the test exercises the existingClaim path with size/storageClass set. + options := &helm.Options{SetValues: map[string]string{ + "telemetry.redis.persistence.enabled": "true", + "telemetry.redis.persistence.existingClaim": "my-prebuilt-redis-pvc", + "telemetry.redis.persistence.size": "100Gi", + "telemetry.redis.persistence.storageClass": "gp3", + }} + + s.assertPVCNotRendered(options, "PVC should not render when existingClaim is set, even with size/storageClass") + + // Confirm size/storageClass don't leak into the deployment either. + deploymentOut := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, []string{s.deploymentTpl}) + s.NotContains(deploymentOut, "100Gi", + "size should not leak into the rendered Deployment when existingClaim is set") + s.NotContains(deploymentOut, "storageClassName", + "storageClassName should not appear in the Deployment when existingClaim is set") +} + +// TestPersistenceDisabledBeatsExistingClaim ensures persistence.enabled=false +// remains the kill-switch — even with existingClaim set, the user opting out +// of persistence should yield an emptyDir volume rather than a stale claim +// mount. +func (s *telemetryRedisTemplateTest) TestPersistenceDisabledBeatsExistingClaim() { + options := &helm.Options{SetValues: map[string]string{ + "telemetry.redis.persistence.enabled": "false", + "telemetry.redis.persistence.existingClaim": "my-prebuilt-redis-pvc", + }} + + s.assertPVCNotRendered(options, "PVC should not render when persistence.enabled=false") + + deployment := s.renderDeployment(options) + volumes := deployment.Spec.Template.Spec.Volumes + s.Require().Len(volumes, 1) + s.NotNil(volumes[0].EmptyDir, + "persistence.enabled=false should take precedence and yield an emptyDir volume") + s.Nil(volumes[0].PersistentVolumeClaim, + "redis-data volume should NOT reference the existingClaim when persistence is disabled") +} diff --git a/tests/unit/helm/telemetry-rolebinding_test.go b/tests/unit/helm/telemetry-rolebinding_test.go new file mode 100644 index 00000000..04569866 --- /dev/null +++ b/tests/unit/helm/telemetry-rolebinding_test.go @@ -0,0 +1,188 @@ +//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-role.yaml", + "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") +} + +// 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 falls back to the + // chart's main app service account — the SA used by sidecars on + // fiftyone-app, teams-plugins, and delegated-operator workloads. The + // teams-api SA is intentionally NOT bound here; api-role.yaml already + // grants it pods/log GET. + 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 bind only to the main app SA") + s.Equal("ServiceAccount", rb.Subjects[0].Kind) + s.Equal("fiftyone-teams", rb.Subjects[0].Namespace) + // Main app SA: chart default serviceAccount.name = "fiftyone-teams" + s.Equal("fiftyone-teams", rb.Subjects[0].Name) +} + +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/telemetry-sidecar_test.go b/tests/unit/helm/telemetry-sidecar_test.go new file mode 100644 index 00000000..5a926a28 --- /dev/null +++ b/tests/unit/helm/telemetry-sidecar_test.go @@ -0,0 +1,197 @@ +//go:build kubeall || helm || unit || unitTelemetrySidecar +// +build kubeall helm unit unitTelemetrySidecar + +package unit + +import ( + "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 telemetrySidecarTemplateTest struct { + suite.Suite + chartPath string + releaseName string + namespace string +} + +func TestTelemetrySidecarTemplate(t *testing.T) { + t.Parallel() + + helmChartPath, err := filepath.Abs(chartPath) + require.NoError(t, err) + + suite.Run(t, &telemetrySidecarTemplateTest{ + Suite: suite.Suite{}, + chartPath: helmChartPath, + releaseName: "fiftyone-test", + namespace: "fiftyone-" + strings.ToLower(random.UniqueId()), + }) +} + +// findSidecar locates the telemetry-sidecar container in a slice of containers. +func findSidecar(containers []corev1.Container) *corev1.Container { + for i, c := range containers { + if c.Name == "telemetry-sidecar" { + return &containers[i] + } + } + return nil +} + +// TestShareProcessNamespaceEnabledByDefault asserts that the api, app, +// plugins, and delegated-operator deployments all opt into PID-namespace +// sharing when telemetry is enabled (the default). The sidecar relies on +// /proc//fd/1 access in the target container's PID namespace, so +// dropping this would silently break log capture. +func (s *telemetrySidecarTemplateTest) TestShareProcessNamespaceEnabledByDefault() { + cases := []struct { + template string + values map[string]string + }{ + {"templates/api-deployment.yaml", nil}, + {"templates/app-deployment.yaml", nil}, + { + "templates/plugins-deployment.yaml", + map[string]string{"pluginsSettings.enabled": "true"}, + }, + { + "templates/delegated-operator-instance-deployment.yaml", + map[string]string{ + "delegatedOperatorDeployments.deployments.teamsDoCpuDefault.enabled": "true", + }, + }, + } + + for _, tc := range cases { + tc := tc + s.Run(tc.template, func() { + options := &helm.Options{SetValues: tc.values} + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, + []string{tc.template}) + + var deployment appsv1.Deployment + helm.UnmarshalK8SYaml(s.T(), output, &deployment) + + s.Require().NotNil(deployment.Spec.Template.Spec.ShareProcessNamespace, + "shareProcessNamespace should be set on %s", tc.template) + s.True(*deployment.Spec.Template.Spec.ShareProcessNamespace, + "shareProcessNamespace should be true on %s", tc.template) + }) + } +} + +// TestShareProcessNamespaceDisabledWithTelemetryOff asserts that +// shareProcessNamespace is NOT set on workloads when telemetry is +// disabled — otherwise we'd be punching an unnecessary hole in pod +// isolation for users who opt out of telemetry. +func (s *telemetrySidecarTemplateTest) TestShareProcessNamespaceDisabledWithTelemetryOff() { + cases := []struct { + template string + values map[string]string + }{ + {"templates/api-deployment.yaml", nil}, + {"templates/app-deployment.yaml", nil}, + { + "templates/plugins-deployment.yaml", + map[string]string{"pluginsSettings.enabled": "true"}, + }, + { + "templates/delegated-operator-instance-deployment.yaml", + map[string]string{ + "delegatedOperatorDeployments.deployments.teamsDoCpuDefault.enabled": "true", + }, + }, + } + + for _, tc := range cases { + tc := tc + s.Run(tc.template, func() { + options := &helm.Options{SetValues: disableTelemetry(tc.values)} + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, + []string{tc.template}) + + var deployment appsv1.Deployment + helm.UnmarshalK8SYaml(s.T(), output, &deployment) + + if deployment.Spec.Template.Spec.ShareProcessNamespace != nil { + s.False(*deployment.Spec.Template.Spec.ShareProcessNamespace, + "shareProcessNamespace should not be true on %s when telemetry is disabled", + tc.template) + } + }) + } +} + +// TestSidecarSecurityContext asserts the sidecar drops all default caps, +// disables privilege escalation, and only adds SYS_PTRACE on executor (DO) +// sidecars where py-spy crash-stack archives are load-bearing. +func (s *telemetrySidecarTemplateTest) TestSidecarSecurityContext() { + cases := []struct { + template string + values map[string]string + executor bool + }{ + {"templates/api-deployment.yaml", nil, false}, + {"templates/app-deployment.yaml", nil, false}, + { + "templates/plugins-deployment.yaml", + map[string]string{"pluginsSettings.enabled": "true"}, + false, + }, + { + "templates/delegated-operator-instance-deployment.yaml", + map[string]string{ + "delegatedOperatorDeployments.deployments.teamsDoCpuDefault.enabled": "true", + }, + true, + }, + } + + for _, tc := range cases { + tc := tc + s.Run(tc.template, func() { + options := &helm.Options{SetValues: tc.values} + output := helm.RenderTemplate(s.T(), options, s.chartPath, s.releaseName, + []string{tc.template}) + + var deployment appsv1.Deployment + helm.UnmarshalK8SYaml(s.T(), output, &deployment) + + sidecar := findSidecar(deployment.Spec.Template.Spec.Containers) + s.Require().NotNil(sidecar, "telemetry-sidecar container should be injected into %s", tc.template) + + sc := sidecar.SecurityContext + s.Require().NotNil(sc, "telemetry-sidecar should have a securityContext") + + s.Require().NotNil(sc.AllowPrivilegeEscalation, "allowPrivilegeEscalation should be set") + s.False(*sc.AllowPrivilegeEscalation, "sidecar must not allow privilege escalation") + + s.Require().NotNil(sc.Capabilities, "Capabilities should be set on sidecar") + s.Contains(sc.Capabilities.Drop, corev1.Capability("ALL"), + "sidecar must drop all default capabilities") + + var hasPtrace bool + for _, capability := range sc.Capabilities.Add { + if capability == "SYS_PTRACE" { + hasPtrace = true + break + } + } + if tc.executor { + s.True(hasPtrace, "executor sidecar must add SYS_PTRACE for py-spy crash archives") + } else { + s.False(hasPtrace, "service-mode sidecar must not add SYS_PTRACE") + } + }) + } +} diff --git a/tests/unit/helm/test_data/delegated-operator-job-configmap_test/expected-cpu-default-override-template-values.yaml b/tests/unit/helm/test_data/delegated-operator-job-configmap_test/expected-cpu-default-override-template-values.yaml index b5f52a36..c64fabab 100644 --- a/tests/unit/helm/test_data/delegated-operator-job-configmap_test/expected-cpu-default-override-template-values.yaml +++ b/tests/unit/helm/test_data/delegated-operator-job-configmap_test/expected-cpu-default-override-template-values.yaml @@ -33,6 +33,9 @@ spec: restartPolicy: Never serviceAccountName: fiftyone-teams securityContext: + fsGroup: 1000 + runAsGroup: 1000 + runAsNonRoot: true runAsUser: 1000 containers: - name: executor diff --git a/tests/unit/helm/test_data/delegated-operator-job-configmap_test/expected-cpu-default.yaml b/tests/unit/helm/test_data/delegated-operator-job-configmap_test/expected-cpu-default.yaml index 260f6637..5f0afd09 100644 --- a/tests/unit/helm/test_data/delegated-operator-job-configmap_test/expected-cpu-default.yaml +++ b/tests/unit/helm/test_data/delegated-operator-job-configmap_test/expected-cpu-default.yaml @@ -23,6 +23,11 @@ spec: spec: restartPolicy: Never serviceAccountName: fiftyone-teams + securityContext: + fsGroup: 1000 + runAsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 containers: - name: executor image: "voxel51/fiftyone-teams-cv-full:v{{CHART_VERSION}}" diff --git a/tests/unit/helm/test_data/delegated-operator-job-configmap_test/expected-override-example-cascading-template.yaml b/tests/unit/helm/test_data/delegated-operator-job-configmap_test/expected-override-example-cascading-template.yaml index 3af3c3d2..e1354b1c 100644 --- a/tests/unit/helm/test_data/delegated-operator-job-configmap_test/expected-override-example-cascading-template.yaml +++ b/tests/unit/helm/test_data/delegated-operator-job-configmap_test/expected-override-example-cascading-template.yaml @@ -33,6 +33,9 @@ spec: restartPolicy: Never serviceAccountName: fiftyone-teams securityContext: + fsGroup: 1000 + runAsGroup: 1000 + runAsNonRoot: true runAsUser: 1000 containers: - name: executor diff --git a/tests/unit/helm/test_data/delegated-operator-job-configmap_test/expected-override-example-template.yaml b/tests/unit/helm/test_data/delegated-operator-job-configmap_test/expected-override-example-template.yaml index 5dd8e0d4..0f5fcca4 100644 --- a/tests/unit/helm/test_data/delegated-operator-job-configmap_test/expected-override-example-template.yaml +++ b/tests/unit/helm/test_data/delegated-operator-job-configmap_test/expected-override-example-template.yaml @@ -33,6 +33,9 @@ spec: restartPolicy: Never serviceAccountName: fiftyone-teams securityContext: + fsGroup: 1000 + runAsGroup: 1000 + runAsNonRoot: true runAsUser: 3000 containers: - name: executor diff --git a/tests/unit/helm/yaml_helpers.go b/tests/unit/helm/yaml_helpers.go new file mode 100644 index 00000000..de1a1c8d --- /dev/null +++ b/tests/unit/helm/yaml_helpers.go @@ -0,0 +1,31 @@ +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 +} + +// 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 +} diff --git a/utils/bump-fixtures-helm.sh b/utils/bump-fixtures-helm.sh index 9e0eed24..90db16d3 100755 --- a/utils/bump-fixtures-helm.sh +++ b/utils/bump-fixtures-helm.sh @@ -139,6 +139,10 @@ if yq -e ".delegatedOperatorTemplates.jobs" "${file}" >/dev/null; then yq "$yq_flags" ".delegatedOperatorTemplates.jobs.*.image.tag = \"${FIFTYONE_APP_VERSION}\"" "${file}" fi +if yq -e ".telemetry.sidecar.image" "${file}" >/dev/null; then + yq "$yq_flags" ".telemetry.sidecar.image.tag = \"${FIFTYONE_APP_VERSION}\"" "${file}" +fi + # Remove temporary file if dry-run if [[ $DRY_RUN == "true" ]]; then cat "${file}" diff --git a/utils/validate-docker-pulls.sh b/utils/validate-docker-pulls.sh index 2af6b79c..d8d014d3 100755 --- a/utils/validate-docker-pulls.sh +++ b/utils/validate-docker-pulls.sh @@ -14,8 +14,11 @@ 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) "voxel51/fiftyone-app" "voxel51/fiftyone-app-gpt" "voxel51/fiftyone-app-torch" @@ -23,6 +26,7 @@ EXPECTED_IMAGES=( "voxel51/fiftyone-teams-app" "voxel51/fiftyone-teams-cas" "voxel51/fiftyone-teams-cv-full" + "voxel51/telemetry-sidecar" ) . "$(dirname "$0")/common.sh" From 8a9eab233d1063d60b75dd9ad4579412121ee8dd Mon Sep 17 00:00:00 2001 From: voxelbot Date: Thu, 28 May 2026 02:42:15 +0000 Subject: [PATCH 09/10] ci(fixtures): automated bump for integration test fixtures teams-app:v2.19.0-rc.18 api:v2.19.0rc19 app:v2.19.0rc19 cas:v2.19.0-rc.18 --- .../docker/integration_internal_auth.env | 10 +++++----- .../fixtures/docker/integration_legacy_auth.env | 10 +++++----- tests/fixtures/helm/integration_values.yaml | 16 ++++++++-------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/fixtures/docker/integration_internal_auth.env b/tests/fixtures/docker/integration_internal_auth.env index e93f9ab5..4b1245c3 100644 --- a/tests/fixtures/docker/integration_internal_auth.env +++ b/tests/fixtures/docker/integration_internal_auth.env @@ -31,8 +31,8 @@ FIFTYONE_ENV=development CAS_DEBUG="cas:*" # Image tags for existing artifacts -VERSION=v2.19.0rc18 -FIFTYONE_APP_VERSION=v2.19.0rc18 -FIFTYONE_TEAMS_API_VERSION=v2.19.0rc18 -FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.17 -FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.17 +VERSION=v2.19.0rc19 +FIFTYONE_APP_VERSION=v2.19.0rc19 +FIFTYONE_TEAMS_API_VERSION=v2.19.0rc19 +FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.18 +FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.18 diff --git a/tests/fixtures/docker/integration_legacy_auth.env b/tests/fixtures/docker/integration_legacy_auth.env index 71fcddd2..bee7f775 100644 --- a/tests/fixtures/docker/integration_legacy_auth.env +++ b/tests/fixtures/docker/integration_legacy_auth.env @@ -39,8 +39,8 @@ FIFTYONE_ENV=development CAS_DEBUG="cas:*" # Image tags for existing artifacts -VERSION=v2.19.0rc18 -FIFTYONE_APP_VERSION=v2.19.0rc18 -FIFTYONE_TEAMS_API_VERSION=v2.19.0rc18 -FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.17 -FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.17 +VERSION=v2.19.0rc19 +FIFTYONE_APP_VERSION=v2.19.0rc19 +FIFTYONE_TEAMS_API_VERSION=v2.19.0rc19 +FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.18 +FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.18 diff --git a/tests/fixtures/helm/integration_values.yaml b/tests/fixtures/helm/integration_values.yaml index 3f17d048..297515fd 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.0rc18 + tag: v2.19.0rc19 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.0rc18 + tag: v2.19.0rc19 # 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.17 + tag: v2.19.0-rc.18 delegatedOperatorDeployments: deployments: teamsDoCpuDefault: image: repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.19.0rc18 + tag: v2.19.0rc19 delegatedOperatorTemplates: jobs: teamsDoCpuDefaultK8s: image: repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.19.0rc18 + tag: v2.19.0rc19 ingress: annotations: # For using the nginx-ingress controller with cert-manager self signed certificates @@ -121,13 +121,13 @@ 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.0rc18 + tag: v2.19.0rc19 telemetry: sidecar: image: repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/telemetry-sidecar pullPolicy: IfNotPresent - tag: v2.19.0rc18 + tag: v2.19.0rc19 teamsAppSettings: dnsName: local.fiftyone.ai # env: @@ -144,4 +144,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.17 + tag: v2.19.0-rc.18 From 7f82001a11815c886560fd4482133debf4bada53 Mon Sep 17 00:00:00 2001 From: voxelbot Date: Thu, 28 May 2026 19:10:22 +0000 Subject: [PATCH 10/10] ci(fixtures): automated bump for integration test fixtures teams-app:v2.19.0 api:v2.19.0 app:v2.19.0 cas:v2.19.0 --- .../docker/integration_internal_auth.env | 10 +++++----- .../fixtures/docker/integration_legacy_auth.env | 10 +++++----- tests/fixtures/helm/integration_values.yaml | 16 ++++++++-------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/fixtures/docker/integration_internal_auth.env b/tests/fixtures/docker/integration_internal_auth.env index 4b1245c3..5e49c2c4 100644 --- a/tests/fixtures/docker/integration_internal_auth.env +++ b/tests/fixtures/docker/integration_internal_auth.env @@ -31,8 +31,8 @@ FIFTYONE_ENV=development CAS_DEBUG="cas:*" # Image tags for existing artifacts -VERSION=v2.19.0rc19 -FIFTYONE_APP_VERSION=v2.19.0rc19 -FIFTYONE_TEAMS_API_VERSION=v2.19.0rc19 -FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.18 -FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.18 +VERSION=v2.19.0 +FIFTYONE_APP_VERSION=v2.19.0 +FIFTYONE_TEAMS_API_VERSION=v2.19.0 +FIFTYONE_TEAMS_APP_VERSION=v2.19.0 +FIFTYONE_TEAMS_CAS_VERSION=v2.19.0 diff --git a/tests/fixtures/docker/integration_legacy_auth.env b/tests/fixtures/docker/integration_legacy_auth.env index bee7f775..626de4f4 100644 --- a/tests/fixtures/docker/integration_legacy_auth.env +++ b/tests/fixtures/docker/integration_legacy_auth.env @@ -39,8 +39,8 @@ FIFTYONE_ENV=development CAS_DEBUG="cas:*" # Image tags for existing artifacts -VERSION=v2.19.0rc19 -FIFTYONE_APP_VERSION=v2.19.0rc19 -FIFTYONE_TEAMS_API_VERSION=v2.19.0rc19 -FIFTYONE_TEAMS_APP_VERSION=v2.19.0-rc.18 -FIFTYONE_TEAMS_CAS_VERSION=v2.19.0-rc.18 +VERSION=v2.19.0 +FIFTYONE_APP_VERSION=v2.19.0 +FIFTYONE_TEAMS_API_VERSION=v2.19.0 +FIFTYONE_TEAMS_APP_VERSION=v2.19.0 +FIFTYONE_TEAMS_CAS_VERSION=v2.19.0 diff --git a/tests/fixtures/helm/integration_values.yaml b/tests/fixtures/helm/integration_values.yaml index 297515fd..50e0a2ae 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.0rc19 + tag: v2.19.0 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.0rc19 + tag: v2.19.0 # 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.18 + tag: v2.19.0 delegatedOperatorDeployments: deployments: teamsDoCpuDefault: image: repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.19.0rc19 + tag: v2.19.0 delegatedOperatorTemplates: jobs: teamsDoCpuDefaultK8s: image: repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/fiftyone-app pullPolicy: IfNotPresent - tag: v2.19.0rc19 + tag: v2.19.0 ingress: annotations: # For using the nginx-ingress controller with cert-manager self signed certificates @@ -121,13 +121,13 @@ 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.0rc19 + tag: v2.19.0 telemetry: sidecar: image: repository: us-central1-docker.pkg.dev/computer-vision-team/dev-docker/telemetry-sidecar pullPolicy: IfNotPresent - tag: v2.19.0rc19 + tag: v2.19.0 teamsAppSettings: dnsName: local.fiftyone.ai # env: @@ -144,4 +144,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.18 + tag: v2.19.0