diff --git a/charts/mlrun-ce/Chart.yaml b/charts/mlrun-ce/Chart.yaml index d79d5aa5..a4455808 100644 --- a/charts/mlrun-ce/Chart.yaml +++ b/charts/mlrun-ce/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v1 name: mlrun-ce -version: 0.10.1-rc3 +version: 0.10.1-rc4 description: MLRun Open Source Stack home: https://iguazio.com icon: https://www.iguazio.com/wp-content/uploads/2019/10/Iguazio-Logo.png diff --git a/charts/mlrun-ce/admin_installation_values.yaml b/charts/mlrun-ce/admin_installation_values.yaml index 7f148948..c9aae751 100644 --- a/charts/mlrun-ce/admin_installation_values.yaml +++ b/charts/mlrun-ce/admin_installation_values.yaml @@ -48,7 +48,7 @@ pipelines: kube-prometheus-stack: enabled: false -tdengine: +timescaledb: enabled: false kafka: diff --git a/charts/mlrun-ce/templates/_helpers.tpl b/charts/mlrun-ce/templates/_helpers.tpl index 2cbca968..3d9bc99a 100644 --- a/charts/mlrun-ce/templates/_helpers.tpl +++ b/charts/mlrun-ce/templates/_helpers.tpl @@ -249,23 +249,25 @@ Model monitoring DSN {{- end -}} {{- end -}} +{{/* +TimescaleDB helpers +*/}} + {{/* Expand the name of the chart. */}} -{{- define "mlrun-ce.tdengine.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- define "mlrun-ce.timescaledb.name" -}} +{{- default "timescaledb" .Values.timescaledb.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. */}} -{{- define "mlrun-ce.tdengine.fullname" -}} -{{- if .Values.tdengine.fullnameOverride }} -{{- .Values.tdengine.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- define "mlrun-ce.timescaledb.fullname" -}} +{{- if .Values.timescaledb.fullnameOverride }} +{{- .Values.timescaledb.fullnameOverride | trunc 63 | trimSuffix "-" }} {{- else }} - {{- $name := default .Chart.Name .Values.tdengine.nameOverride }} + {{- $name := default "timescaledb" .Values.timescaledb.nameOverride }} {{- if contains $name .Release.Name }} {{- .Release.Name | trunc 63 | trimSuffix "-" }} {{- else }} @@ -277,16 +279,16 @@ If release name contains chart name it will be used as a full name. {{/* Create chart name and version as used by the chart label. */}} -{{- define "mlrun-ce.tdengine.chart" -}} +{{- define "mlrun-ce.timescaledb.chart" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} {{- end }} {{/* -Common labels +TimescaleDB Common labels */}} -{{- define "mlrun-ce.tdengine.labels" -}} -helm.sh/chart: {{ include "mlrun-ce.tdengine.chart" . }} -{{ include "mlrun-ce.tdengine.selectorLabels" . }} +{{- define "mlrun-ce.timescaledb.labels" -}} +helm.sh/chart: {{ include "mlrun-ce.timescaledb.chart" . }} +{{ include "mlrun-ce.timescaledb.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} @@ -294,10 +296,18 @@ app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }} {{/* -Selector labels +TimescaleDB Selector labels */}} -{{- define "mlrun-ce.tdengine.selectorLabels" -}} -app.kubernetes.io/name: {{ include "mlrun-ce.tdengine.name" . }} +{{- define "mlrun-ce.timescaledb.selectorLabels" -}} +app.kubernetes.io/name: {{ include "mlrun-ce.timescaledb.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/component: timescaledb +{{- end }} + +{{/* +TimescaleDB connection string for MLRun model monitoring +*/}} +{{- define "mlrun-ce.timescaledb.connectionString" -}} +postgresql://{{ .Values.timescaledb.auth.username }}:{{ .Values.timescaledb.auth.password }}@{{ include "mlrun-ce.timescaledb.fullname" . }}:{{ .Values.timescaledb.service.port }}/{{ .Values.timescaledb.auth.database }} {{- end }} diff --git a/charts/mlrun-ce/templates/tdengine/configmap.yaml b/charts/mlrun-ce/templates/tdengine/configmap.yaml deleted file mode 100644 index 2e7703bc..00000000 --- a/charts/mlrun-ce/templates/tdengine/configmap.yaml +++ /dev/null @@ -1,13 +0,0 @@ -{{- if .Values.tdengine.enabled -}} ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "mlrun-ce.tdengine.fullname" . }}-taoscfg - labels: - {{- include "mlrun-ce.tdengine.labels" . | nindent 4 }} -data: -{{- with .Values.tdengine.taoscfg }} - {{- toYaml . | nindent 2 }} -{{- end }} -{{- end }} diff --git a/charts/mlrun-ce/templates/tdengine/service.yaml b/charts/mlrun-ce/templates/tdengine/service.yaml deleted file mode 100644 index 712b1ff3..00000000 --- a/charts/mlrun-ce/templates/tdengine/service.yaml +++ /dev/null @@ -1,23 +0,0 @@ -{{- if .Values.tdengine.enabled -}} -apiVersion: v1 -kind: Service -metadata: - name: {{ include "mlrun-ce.tdengine.fullname" . }} - labels: - {{- include "mlrun-ce.tdengine.labels" . | nindent 4 }} -spec: - type: {{ .Values.tdengine.service.type }} - ports: {{ range $idx, $val := .Values.tdengine.service.ports.tcp }} - - name: tcp{{- $idx}} - port: {{ $val }} - protocol: TCP - {{- end }} - {{ range $idx, $val := .Values.tdengine.service.ports.udp }} - - name: udp {{- $idx}} - port: {{ $val }} - protocol: UDP - {{- end }} - selector: - {{- include "mlrun-ce.tdengine.selectorLabels" . | nindent 4 }} - app: "taosd" -{{- end -}} diff --git a/charts/mlrun-ce/templates/tdengine/statefulset.yaml b/charts/mlrun-ce/templates/tdengine/statefulset.yaml deleted file mode 100644 index 017a90da..00000000 --- a/charts/mlrun-ce/templates/tdengine/statefulset.yaml +++ /dev/null @@ -1,149 +0,0 @@ -{{- if .Values.tdengine.enabled -}} -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: {{ include "mlrun-ce.tdengine.fullname" . }} - labels: - {{- include "mlrun-ce.tdengine.labels" . | nindent 4 }} - app: taosd -spec: - serviceName: {{ include "mlrun-ce.tdengine.fullname" . }} - replicas: {{ .Values.tdengine.replicaCount }} - # podManagementPolicy: Parallel - selector: - matchLabels: - {{- include "mlrun-ce.tdengine.selectorLabels" . | nindent 6 }} - app: taosd - template: - metadata: - {{- with .Values.tdengine.podAnnotations }} - annotations: - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include "mlrun-ce.tdengine.selectorLabels" . | nindent 8 }} - app: taosd - spec: - {{- with .Values.tdengine.nodeSelectors.taosd }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tdengine.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - containers: - - name: {{ include "mlrun-ce.tdengine.fullname" . }} - image: "{{ .Values.tdengine.image.prefix }}:{{ .Values.tdengine.image.tag | default .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.tdengine.image.pullPolicy }} - ports: {{ range $idx, $val := .Values.tdengine.service.ports.tcp }} - - name: tcp{{- $idx}} - containerPort: {{ $val }} - protocol: TCP - {{- end }} - {{ range $idx, $val := .Values.tdengine.service.ports.udp }} - - name: udp {{- $idx}} - containerPort: {{ $val }} - protocol: UDP - {{- end }} - - env: - # POD_NAME for FQDN config - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - # SERVICE_NAME and NAMESPACE for fqdn resolve - - name: SERVICE_NAME - value: {{ include "mlrun-ce.tdengine.fullname" . }} - - name: STS_NAME - value: {{ include "mlrun-ce.tdengine.fullname" . }} - - name: STS_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - # TZ for timezone settings, we recommend to always set it. - - name: TZ - value: {{ .Values.tdengine.timezone }} - # TAOS_ prefix will configured in taos.cfg, strip prefix and camelCase. - - name: TAOS_SERVER_PORT - value: "6030" - # Must set if you want a cluster. - - name: TAOS_FIRST_EP - value: '$(STS_NAME)-0.$(SERVICE_NAME).$(STS_NAMESPACE).svc.{{ .Values.clusterDomainSuffix | default "cluster.local" }}:$(TAOS_SERVER_PORT)' - # TAOS_FQND should always be setted in k8s env. - - name: TAOS_FQDN - value: '$(POD_NAME).$(SERVICE_NAME).$(STS_NAMESPACE).svc.{{ .Values.clusterDomainSuffix | default "cluster.local" }}' - - envFrom: - - configMapRef: - name: {{ include "mlrun-ce.tdengine.fullname" . }}-taoscfg - volumeMounts: - - name: {{ include "mlrun-ce.tdengine.fullname" . }}-taosdata - mountPath: /var/lib/taos - - name: {{ include "mlrun-ce.tdengine.fullname" . }}-taoslog - mountPath: /var/log/taos - readinessProbe: - exec: - command: - - taos-check - initialDelaySeconds: 5 - timeoutSeconds: 5000 - livenessProbe: - exec: - command: - - taos-check - initialDelaySeconds: 15 - periodSeconds: 20 - securityContext: - # privileged: true - # allowPrivilegeEscalation: true - # runAsUser: 0 - # runAsGroup: 0 - # readOnlyRootFilesystem: false - # allowedCapabilities: - # - CAP_SYS_ADMIN - # - CHOWN - # - DAC_OVERRIDE - # - SETGID - # - SETUID - # - NET_BIND_SERVICE - # AllowedHostPaths: - # - pathPrefix: "/proc" - # readOnly: true # 仅允许只读模式挂载 - # - pathPrefix: "/sys" - # readOnly: true # 仅允许只读模式挂载 - resources: - {{- toYaml .Values.tdengine.resources | nindent 12 }} - {{- with .Values.tdengine.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tdengine.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} - volumeClaimTemplates: - - metadata: - name: {{ include "mlrun-ce.tdengine.fullname" . }}-taosdata - spec: - accessModes: - - "ReadWriteOnce" - {{- if .Values.tdengine.storage.className }} - storageClassName: "{{ .Values.tdengine.storage.className }}" - {{- end }} - resources: - requests: - storage: "{{ .Values.tdengine.storage.dataSize }}" - - metadata: - name: {{ include "mlrun-ce.tdengine.fullname" . }}-taoslog - spec: - accessModes: - - "ReadWriteOnce" - {{- if .Values.tdengine.storage.className }} - storageClassName: "{{ .Values.tdengine.storage.className }}" - {{- end }} - resources: - requests: - storage: "{{ .Values.tdengine.storage.logSize }}" -{{- end }} diff --git a/charts/mlrun-ce/templates/timescaledb/secret.yaml b/charts/mlrun-ce/templates/timescaledb/secret.yaml new file mode 100644 index 00000000..13367a6c --- /dev/null +++ b/charts/mlrun-ce/templates/timescaledb/secret.yaml @@ -0,0 +1,12 @@ +{{- if .Values.timescaledb.enabled -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "mlrun-ce.timescaledb.fullname" . }}-secret + labels: + {{- include "mlrun-ce.timescaledb.labels" . | nindent 4 }} +type: Opaque +stringData: + username: {{ .Values.timescaledb.auth.username | quote }} + password: {{ .Values.timescaledb.auth.password | quote }} +{{- end }} diff --git a/charts/mlrun-ce/templates/timescaledb/service.yaml b/charts/mlrun-ce/templates/timescaledb/service.yaml new file mode 100644 index 00000000..d1050c7b --- /dev/null +++ b/charts/mlrun-ce/templates/timescaledb/service.yaml @@ -0,0 +1,17 @@ +{{- if .Values.timescaledb.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "mlrun-ce.timescaledb.fullname" . }} + labels: + {{- include "mlrun-ce.timescaledb.labels" . | nindent 4 }} +spec: + type: {{ .Values.timescaledb.service.type }} + ports: + - name: postgresql + port: {{ .Values.timescaledb.service.port }} + targetPort: postgresql + protocol: TCP + selector: + {{- include "mlrun-ce.timescaledb.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/charts/mlrun-ce/templates/timescaledb/statefulset.yaml b/charts/mlrun-ce/templates/timescaledb/statefulset.yaml new file mode 100644 index 00000000..0ea0939d --- /dev/null +++ b/charts/mlrun-ce/templates/timescaledb/statefulset.yaml @@ -0,0 +1,97 @@ +{{- if .Values.timescaledb.enabled -}} +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "mlrun-ce.timescaledb.fullname" . }} + labels: + {{- include "mlrun-ce.timescaledb.labels" . | nindent 4 }} +spec: + serviceName: {{ include "mlrun-ce.timescaledb.fullname" . }} + replicas: 1 + selector: + matchLabels: + {{- include "mlrun-ce.timescaledb.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "mlrun-ce.timescaledb.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.timescaledb.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.timescaledb.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: timescaledb + image: "{{ .Values.timescaledb.image.repository }}:{{ .Values.timescaledb.image.tag }}" + imagePullPolicy: {{ .Values.timescaledb.image.pullPolicy }} + ports: + - name: postgresql + containerPort: 5432 + protocol: TCP + env: + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: {{ include "mlrun-ce.timescaledb.fullname" . }}-secret + key: username + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "mlrun-ce.timescaledb.fullname" . }}-secret + key: password + - name: POSTGRES_DB + value: {{ .Values.timescaledb.auth.database | quote }} + - name: PGDATA + value: /var/lib/postgresql/data/pgdata + volumeMounts: + - name: data + mountPath: /var/lib/postgresql/data + livenessProbe: + exec: + command: + - pg_isready + - -U + - {{ .Values.timescaledb.auth.username }} + - -d + - {{ .Values.timescaledb.auth.database }} + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + readinessProbe: + exec: + command: + - pg_isready + - -U + - {{ .Values.timescaledb.auth.username }} + - -d + - {{ .Values.timescaledb.auth.database }} + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + resources: + {{- toYaml .Values.timescaledb.resources | nindent 12 }} + {{- if .Values.timescaledb.persistence.enabled }} + volumeClaimTemplates: + - metadata: + name: data + {{- with .Values.timescaledb.persistence.annotations }} + annotations: + {{- toYaml . | nindent 10 }} + {{- end }} + spec: + accessModes: + - {{ .Values.timescaledb.persistence.accessMode | quote }} + {{- if .Values.timescaledb.persistence.storageClass }} + storageClassName: {{ .Values.timescaledb.persistence.storageClass | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.timescaledb.persistence.size | quote }} + {{- end }} +{{- end }} diff --git a/charts/mlrun-ce/values.yaml b/charts/mlrun-ce/values.yaml index 8ecf30f4..bdcf1aec 100644 --- a/charts/mlrun-ce/values.yaml +++ b/charts/mlrun-ce/values.yaml @@ -458,34 +458,36 @@ prometheus-node-exporter: hostRootFsMount: enabled: false -tdengine: +timescaledb: enabled: true - fullnameOverride: tdengine-tsdb - # For more detailes - # https://github.com/taosdata/TDengine-Operator/blob/3.0/helm/tdengine/values.yaml - replicaCount: 1 + fullnameOverride: timescaledb image: - prefix: tdengine/tdengine - pullPolicy: Always - tag: "3.0.7.1" + repository: timescale/timescaledb-ha + tag: "pg17" + pullPolicy: IfNotPresent service: type: ClusterIP - ports: - tcp: [6030, 6041, 6042, 6043, 6044, 6046, 6047, 6048, 6049, 6060] - udp: [6044, 6045] - # Set timezone here, not in taoscfg - timezone: "UTC" + port: 5432 + auth: + username: postgres + password: postgres + database: postgres + persistence: + enabled: true + size: "10Gi" + storageClass: "" + accessMode: "ReadWriteOnce" + annotations: + helm.sh/resource-policy: "keep" resources: - storage: - className: "" - dataSize: "10Gi" - logSize: "1Gi" - nodeSelectors: - taosd: - clusterDomainSuffix: "" - taoscfg: - CLUSTER: "0" - TAOS_REPLICA: "1" + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" + nodeSelector: {} + tolerations: [] kafka: global: diff --git a/tests/kind-test.sh b/tests/kind-test.sh new file mode 100755 index 00000000..33b3f4c1 --- /dev/null +++ b/tests/kind-test.sh @@ -0,0 +1,292 @@ +#!/usr/bin/env bash +# Copyright 2025 Iguazio +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Local Kind cluster test for MLRun CE Helm chart +# Requirements: docker, kind, kubectl, helm + +set -o errexit +set -o nounset +set -o pipefail + +CLUSTER_NAME="${CLUSTER_NAME:-mlrun-ce-test}" +NAMESPACE="${NAMESPACE:-mlrun}" +RELEASE_NAME="${RELEASE_NAME:-mlrun}" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CHART_DIR="${SCRIPT_DIR}/../charts/mlrun-ce" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +log_info() { echo -e "${GREEN}[INFO]${NC} $1"; } +log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } +log_error() { echo -e "${RED}[ERROR]${NC} $1"; } + +cleanup() { + if [[ "${CLEANUP_ON_EXIT:-false}" == "true" ]]; then + log_info "Cleaning up Kind cluster..." + kind delete cluster --name "${CLUSTER_NAME}" 2>/dev/null || true + fi +} + +trap cleanup EXIT + +check_requirements() { + local missing=() + local tool + for tool in docker kind kubectl helm; do + if ! command -v "$tool" &> /dev/null; then + missing+=("$tool") + fi + done + if [[ ${#missing[@]} -gt 0 ]]; then + log_error "Missing required tools: ${missing[*]}" + exit 1 + fi +} + +create_kind_cluster() { + if kind get clusters 2>/dev/null | grep -q "^${CLUSTER_NAME}$"; then + log_warn "Cluster '${CLUSTER_NAME}' already exists" + return 0 + fi + + log_info "Creating Kind cluster '${CLUSTER_NAME}'..." + cat </dev/null || true + helm repo add mlrun https://v3io.github.io/helm-charts/stable 2>/dev/null || true + helm repo add mpi-operator https://v3io.github.io/helm-charts/stable 2>/dev/null || true + helm repo add minio https://charts.min.io/ 2>/dev/null || true + helm repo add spark-operator https://kubeflow.github.io/spark-operator 2>/dev/null || true + helm repo add kube-prometheus-stack https://prometheus-community.github.io/helm-charts 2>/dev/null || true + helm repo add kafka https://charts.bitnami.com/bitnami 2>/dev/null || true + helm repo update +} + +build_dependencies() { + log_info "Building chart dependencies..." + helm dependency update "${CHART_DIR}" +} + +install_chart() { + log_info "Installing MLRun CE chart..." + + # Create minimal values for local testing + local values_file + values_file=$(mktemp) + cat > "${values_file}" </dev/null || echo "") + + if [[ -n "${tsdb_pod}" ]]; then + log_info "TimescaleDB pod: ${tsdb_pod}" + kubectl exec -n "${NAMESPACE}" "${tsdb_pod}" -- psql -U postgres -c "SELECT version();" 2>/dev/null || log_warn "Could not query PostgreSQL version" + kubectl exec -n "${NAMESPACE}" "${tsdb_pod}" -- psql -U postgres -c "SELECT extversion FROM pg_extension WHERE extname='timescaledb';" 2>/dev/null || log_warn "Could not query TimescaleDB version" + else + log_warn "TimescaleDB pod not found" + fi +} + +delete_cluster() { + log_info "Deleting Kind cluster '${CLUSTER_NAME}'..." + kind delete cluster --name "${CLUSTER_NAME}" +} + +usage() { + cat <