From e97924ae5944a9b1fb44d6fa8e966ad9fd79dbbf Mon Sep 17 00:00:00 2001 From: Shimi Bandiel Date: Tue, 21 Jul 2026 21:26:33 +0000 Subject: [PATCH 1/5] feat!: rename Helm chart async-processor -> llm-d-async (#179) Rename the Helm chart (name, directory, template helpers, rendered resource names/labels, unit tests) and all in-repo consumers: publish-helm-chart.sh, CI workflows (helm lint/unittest/template paths), deploy/install.sh + kind-emulator, docs/guides/e2e-deploy, and the e2e suite. The container image was already llm-d-async, so this completes #179. BREAKING CHANGE: the chart now publishes as oci://ghcr.io/llm-d/charts/llm-d-async and renders resources named llm-d-async-* with app.kubernetes.io/name=llm-d-async. A Deployment selector is immutable, so upgrading from an existing async-processor install requires uninstall + reinstall. The old charts/async-processor OCI package is not renamed (GHCR has no redirect); its last version stays available but receives no new releases. Also fixes deploy/install.sh's default image repo, which pointed at the nonexistent ghcr.io/llm-d/async-processor -> ghcr.io/llm-d/llm-d-async. Out of scope (unchanged): the Go binary name, the OTEL service name (async-processor), and the *-system / *-monitoring namespace defaults. Closes #179 Signed-off-by: Shimi Bandiel --- .github/workflows/ci-e2e-tests.yaml | 2 +- .github/workflows/pre-commit.yml | 4 +- .../Chart.yaml | 2 +- .../dashboards/llm-d-async.json} | 2 +- .../templates/_helpers.tpl | 28 ++++---- .../templates/ap-configmap.yaml | 4 +- .../templates/ap-deployments.yaml | 34 +++++----- .../templates/ap-podmonitor.yaml | 6 +- .../templates/ap-serviceaccounts.yaml | 4 +- .../grafana-dashboards-configmap.yaml | 4 +- .../templates/modelserver-podmonitor.yaml | 4 +- .../templates/prometheus-rule.yaml | 6 +- .../templates/redis-secret.yaml | 4 +- .../tests/deployment_test.yaml | 20 +++--- .../tests/observability_test.yaml | 2 +- .../values.yaml | 2 +- deploy/install.sh | 20 +++--- docs/guides/e2e-deploy.md | 24 +++---- ...or-values.yaml => llm-d-async-values.yaml} | 0 scripts/publish-helm-chart.sh | 12 ++-- test/e2e/e2e_endpoint_scrape_gate_test.go | 2 +- test/e2e/e2e_health_test.go | 2 +- test/e2e/e2e_multitenant_test.go | 2 +- test/e2e/e2e_saturation_gate_test.go | 2 +- test/e2e/e2e_suite_test.go | 68 +++++++++---------- test/e2e/e2e_test.go | 14 ++-- test/e2e/utils_test.go | 2 +- ...posite.yaml => llm-d-async-composite.yaml} | 12 ++-- ...ssor-quota.yaml => llm-d-async-quota.yaml} | 12 ++-- 29 files changed, 150 insertions(+), 150 deletions(-) rename charts/{async-processor => llm-d-async}/Chart.yaml (88%) rename charts/{async-processor/dashboards/async-processor.json => llm-d-async/dashboards/llm-d-async.json} (99%) rename charts/{async-processor => llm-d-async}/templates/_helpers.tpl (73%) rename charts/{async-processor => llm-d-async}/templates/ap-configmap.yaml (86%) rename charts/{async-processor => llm-d-async}/templates/ap-deployments.yaml (90%) rename charts/{async-processor => llm-d-async}/templates/ap-podmonitor.yaml (70%) rename charts/{async-processor => llm-d-async}/templates/ap-serviceaccounts.yaml (74%) rename charts/{async-processor => llm-d-async}/templates/grafana-dashboards-configmap.yaml (72%) rename charts/{async-processor => llm-d-async}/templates/modelserver-podmonitor.yaml (86%) rename charts/{async-processor => llm-d-async}/templates/prometheus-rule.yaml (96%) rename charts/{async-processor => llm-d-async}/templates/redis-secret.yaml (66%) rename charts/{async-processor => llm-d-async}/tests/deployment_test.yaml (95%) rename charts/{async-processor => llm-d-async}/tests/observability_test.yaml (99%) rename charts/{async-processor => llm-d-async}/values.yaml (99%) rename docs/guides/e2e-deploy/{async-processor-values.yaml => llm-d-async-values.yaml} (100%) rename test/e2e/yaml/{async-processor-composite.yaml => llm-d-async-composite.yaml} (83%) rename test/e2e/yaml/{async-processor-quota.yaml => llm-d-async-quota.yaml} (78%) diff --git a/.github/workflows/ci-e2e-tests.yaml b/.github/workflows/ci-e2e-tests.yaml index 984fcc0f..738a0b16 100644 --- a/.github/workflows/ci-e2e-tests.yaml +++ b/.github/workflows/ci-e2e-tests.yaml @@ -83,7 +83,7 @@ jobs: kubectl --kubeconfig="$KUBECONFIG" -n e2e-integration get pods -o wide > /tmp/e2e-artifacts/pods.txt 2>&1 || true kubectl --kubeconfig="$KUBECONFIG" -n e2e-integration describe pods > /tmp/e2e-artifacts/pod-descriptions.txt 2>&1 || true kubectl --kubeconfig="$KUBECONFIG" -n e2e-integration get events --sort-by='.lastTimestamp' > /tmp/e2e-artifacts/events.txt 2>&1 || true - kubectl --kubeconfig="$KUBECONFIG" -n e2e-integration logs -l app.kubernetes.io/name=async-processor --tail=500 > /tmp/e2e-artifacts/processor-logs.txt 2>&1 || true + kubectl --kubeconfig="$KUBECONFIG" -n e2e-integration logs -l app.kubernetes.io/name=llm-d-async --tail=500 > /tmp/e2e-artifacts/processor-logs.txt 2>&1 || true - name: Upload E2E artifacts if: failure() diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index a3f0c04a..58352302 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -46,12 +46,12 @@ jobs: run: make test-integration - name: Lint Helm chart - run: helm lint charts/async-processor + run: helm lint charts/llm-d-async - name: Run Helm unit tests run: | helm plugin install --version v1.0.3 --verify=false https://github.com/helm-unittest/helm-unittest.git - helm unittest charts/async-processor + helm unittest charts/llm-d-async check-code-changes: runs-on: ubuntu-latest diff --git a/charts/async-processor/Chart.yaml b/charts/llm-d-async/Chart.yaml similarity index 88% rename from charts/async-processor/Chart.yaml rename to charts/llm-d-async/Chart.yaml index 7dfd6536..15f76a40 100644 --- a/charts/async-processor/Chart.yaml +++ b/charts/llm-d-async/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -name: async-processor +name: llm-d-async description: Helm chart for Async-Processor (AP) - Asynchronous Processor for LLM inference workloads type: application version: 0.7.0 diff --git a/charts/async-processor/dashboards/async-processor.json b/charts/llm-d-async/dashboards/llm-d-async.json similarity index 99% rename from charts/async-processor/dashboards/async-processor.json rename to charts/llm-d-async/dashboards/llm-d-async.json index 4dee2095..6646cbfb 100644 --- a/charts/async-processor/dashboards/async-processor.json +++ b/charts/llm-d-async/dashboards/llm-d-async.json @@ -185,5 +185,5 @@ }, "time": { "from": "now-1h", "to": "now" }, "title": "Async Processor", - "uid": "async-processor" + "uid": "llm-d-async" } diff --git a/charts/async-processor/templates/_helpers.tpl b/charts/llm-d-async/templates/_helpers.tpl similarity index 73% rename from charts/async-processor/templates/_helpers.tpl rename to charts/llm-d-async/templates/_helpers.tpl index 17789b62..2d8e61c1 100644 --- a/charts/async-processor/templates/_helpers.tpl +++ b/charts/llm-d-async/templates/_helpers.tpl @@ -1,7 +1,7 @@ {{/* Expand the name of the chart. */}} -{{- define "async-processor.name" -}} +{{- define "llm-d-async.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} @@ -10,7 +10,7 @@ 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 "async-processor.fullname" -}} +{{- define "llm-d-async.fullname" -}} {{- if .Values.fullnameOverride }} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} {{- else }} @@ -26,16 +26,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 "async-processor.chart" -}} +{{- define "llm-d-async.chart" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Common labels */}} -{{- define "async-processor.labels" -}} -helm.sh/chart: {{ include "async-processor.chart" . }} -{{ include "async-processor.selectorLabels" . }} +{{- define "llm-d-async.labels" -}} +helm.sh/chart: {{ include "llm-d-async.chart" . }} +{{ include "llm-d-async.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} @@ -45,23 +45,23 @@ app.kubernetes.io/managed-by: {{ .Release.Service }} {{/* Selector labels */}} -{{- define "async-processor.selectorLabels" -}} -app.kubernetes.io/name: {{ include "async-processor.name" . }} +{{- define "llm-d-async.selectorLabels" -}} +app.kubernetes.io/name: {{ include "llm-d-async.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} {{/* Create the name of the service account to use */}} -{{- define "async-processor.serviceAccountName" -}} -{{- default (include "async-processor.fullname" .) .Values.serviceAccount.name }} +{{- define "llm-d-async.serviceAccountName" -}} +{{- default (include "llm-d-async.fullname" .) .Values.serviceAccount.name }} {{- end }} {{/* Render gate params as JSON with all values as strings. The gate params parser expects map[string]string, so numeric values must be quoted. */}} -{{- define "async-processor.gateParamsJson" -}} +{{- define "llm-d-async.gateParamsJson" -}} {{- $out := dict -}} {{- range $k, $v := .Values.ap.redis.gateParams -}} {{- $_ := set $out $k ($v | toString) -}} @@ -74,9 +74,9 @@ Resolve the Redis secret name. If redis.url is set, the chart creates a Secret named -redis. Otherwise, use the user-provided redis.secretName. */}} -{{- define "async-processor.redisSecretName" -}} +{{- define "llm-d-async.redisSecretName" -}} {{- if .Values.ap.redis.url -}} -{{- printf "%s-redis" (include "async-processor.fullname" .) -}} +{{- printf "%s-redis" (include "llm-d-async.fullname" .) -}} {{- else -}} {{- .Values.ap.redis.secretName -}} {{- end -}} @@ -86,7 +86,7 @@ Otherwise, use the user-provided redis.secretName. Resolve the Redis secret key. When the chart creates the Secret, the key is always "url". */}} -{{- define "async-processor.redisSecretKey" -}} +{{- define "llm-d-async.redisSecretKey" -}} {{- if .Values.ap.redis.url -}} url {{- else -}} diff --git a/charts/async-processor/templates/ap-configmap.yaml b/charts/llm-d-async/templates/ap-configmap.yaml similarity index 86% rename from charts/async-processor/templates/ap-configmap.yaml rename to charts/llm-d-async/templates/ap-configmap.yaml index c27b6138..b264a9db 100644 --- a/charts/async-processor/templates/ap-configmap.yaml +++ b/charts/llm-d-async/templates/ap-configmap.yaml @@ -2,10 +2,10 @@ apiVersion: v1 kind: ConfigMap metadata: - name: {{ include "async-processor.fullname" . }}-config + name: {{ include "llm-d-async.fullname" . }}-config namespace: {{ .Release.Namespace }} labels: - {{- include "async-processor.labels" . | nindent 4 }} + {{- include "llm-d-async.labels" . | nindent 4 }} data: {{- if .Values.ap.workerPools }} worker-pools.json: {{ .Values.ap.workerPools | toJson | quote }} diff --git a/charts/async-processor/templates/ap-deployments.yaml b/charts/llm-d-async/templates/ap-deployments.yaml similarity index 90% rename from charts/async-processor/templates/ap-deployments.yaml rename to charts/llm-d-async/templates/ap-deployments.yaml index 12c57b47..222796b4 100644 --- a/charts/async-processor/templates/ap-deployments.yaml +++ b/charts/llm-d-async/templates/ap-deployments.yaml @@ -20,19 +20,19 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: {{ include "async-processor.fullname" . }} + name: {{ include "llm-d-async.fullname" . }} namespace: {{ .Release.Namespace }} labels: - {{- include "async-processor.labels" . | nindent 4 }} + {{- include "llm-d-async.labels" . | nindent 4 }} spec: selector: matchLabels: - {{- include "async-processor.selectorLabels" . | nindent 6 }} + {{- include "llm-d-async.selectorLabels" . | nindent 6 }} replicas: 1 template: metadata: labels: - {{- include "async-processor.selectorLabels" . | nindent 8 }} + {{- include "llm-d-async.selectorLabels" . | nindent 8 }} spec: securityContext: runAsNonRoot: true @@ -40,7 +40,7 @@ spec: type: RuntimeDefault containers: - command: - - /async-processor + - /llm-d-async args: {{- if .Values.ap.redis.enabled }} {{- if eq (.Values.ap.messageQueueImpl | default "redis-pubsub") "redis-sortedset" }} @@ -59,7 +59,7 @@ spec: - "{{ .Values.ap.redis.requestQueueName | default "request-sortedset" }}" {{- if .Values.ap.redis.gateType }} - --redis.ss.gate-type={{ .Values.ap.redis.gateType }} - - --redis.ss.gate-params={{ include "async-processor.gateParamsJson" . }} + - --redis.ss.gate-params={{ include "llm-d-async.gateParamsJson" . }} {{- end }} {{- end }} - --redis.ss.result-queue-name @@ -97,7 +97,7 @@ spec: {{- end }} - --pubsub.result-topic-id={{ .Values.ap.gcpPubSub.resultTopicId }} {{- if .Values.ap.gcpPubSub.topicsConfig }} - - --pubsub.topics-config-file=/etc/async-processor/config/pubsub-topics.json + - --pubsub.topics-config-file=/etc/llm-d-async/config/pubsub-topics.json {{- else }} - --pubsub.igw-base-url={{ .Values.ap.igwBaseURL }} - --pubsub.request-subscriber-id={{ .Values.ap.gcpPubSub.requestSubscriberId }} @@ -112,13 +112,13 @@ spec: - --prometheus-cache-ttl={{ .Values.ap.prometheusCacheTTL }} {{- end }} {{- if .Values.ap.workerPools }} - - --pool-config-file=/etc/async-processor/config/worker-pools.json + - --pool-config-file=/etc/llm-d-async/config/worker-pools.json {{- end }} {{- if .Values.ap.transformConfig }} - - --transform-config-file=/etc/async-processor/config/transform-config.json + - --transform-config-file=/etc/llm-d-async/config/transform-config.json {{- end }} {{- if .Values.ap.requestMergePolicyConfig }} - - --request-merge-policy-config=/etc/async-processor/config/request-merge-policy.json + - --request-merge-policy-config=/etc/llm-d-async/config/request-merge-policy.json {{- end }} - --concurrency={{ .Values.ap.concurrency | default 64 }} - --drain-timeout={{ .Values.ap.drainTimeout | default "2m" }} @@ -161,7 +161,7 @@ spec: - name: OTEL_TRACES_SAMPLER_ARG value: {{ .Values.ap.otel.samplerArg | quote }} - name: OTEL_SERVICE_NAME - value: "async-processor" + value: "llm-d-async" - name: OTEL_RESOURCE_ATTRIBUTES value: "k8s.namespace.name=$(NAMESPACE),k8s.pod.name=$(POD_NAME),service.version={{ .Chart.AppVersion }}" {{- end }} @@ -169,10 +169,10 @@ spec: - name: REDIS_URL valueFrom: secretKeyRef: - name: {{ include "async-processor.redisSecretName" . }} - key: {{ include "async-processor.redisSecretKey" . }} + name: {{ include "llm-d-async.redisSecretName" . }} + key: {{ include "llm-d-async.redisSecretKey" . }} {{- end }} - name: async-processor + name: llm-d-async ports: - name: metrics containerPort: {{ .Values.ap.metrics.port | default 9090 }} @@ -216,7 +216,7 @@ spec: volumeMounts: {{- if or .Values.ap.workerPools .Values.ap.gcpPubSub.topicsConfig .Values.ap.transformConfig .Values.ap.requestMergePolicyConfig }} - name: ap-config - mountPath: /etc/async-processor/config + mountPath: /etc/llm-d-async/config readOnly: true {{- end }} {{- if .Values.ap.tls.secretName }} @@ -225,14 +225,14 @@ spec: readOnly: true {{- end }} {{- end }} - serviceAccountName: {{ include "async-processor.fullname" . }} + serviceAccountName: {{ include "llm-d-async.fullname" . }} terminationGracePeriodSeconds: 130 {{- if or .Values.ap.workerPools .Values.ap.gcpPubSub.topicsConfig .Values.ap.transformConfig .Values.ap.requestMergePolicyConfig .Values.ap.tls.secretName }} volumes: {{- if or .Values.ap.workerPools .Values.ap.gcpPubSub.topicsConfig .Values.ap.transformConfig .Values.ap.requestMergePolicyConfig }} - name: ap-config configMap: - name: {{ include "async-processor.fullname" . }}-config + name: {{ include "llm-d-async.fullname" . }}-config {{- end }} {{- if .Values.ap.tls.secretName }} - name: tls-certs diff --git a/charts/async-processor/templates/ap-podmonitor.yaml b/charts/llm-d-async/templates/ap-podmonitor.yaml similarity index 70% rename from charts/async-processor/templates/ap-podmonitor.yaml rename to charts/llm-d-async/templates/ap-podmonitor.yaml index 3e8cb276..a1def7cd 100644 --- a/charts/async-processor/templates/ap-podmonitor.yaml +++ b/charts/llm-d-async/templates/ap-podmonitor.yaml @@ -2,17 +2,17 @@ apiVersion: monitoring.coreos.com/v1 kind: PodMonitor metadata: - name: {{ include "async-processor.fullname" . }} + name: {{ include "llm-d-async.fullname" . }} namespace: {{ .Release.Namespace }} labels: - {{- include "async-processor.labels" . | nindent 4 }} + {{- include "llm-d-async.labels" . | nindent 4 }} {{- with .Values.ap.podMonitor.labels }} {{- toYaml . | nindent 4 }} {{- end }} spec: selector: matchLabels: - {{- include "async-processor.selectorLabels" . | nindent 6 }} + {{- include "llm-d-async.selectorLabels" . | nindent 6 }} podMetricsEndpoints: - port: metrics path: /metrics diff --git a/charts/async-processor/templates/ap-serviceaccounts.yaml b/charts/llm-d-async/templates/ap-serviceaccounts.yaml similarity index 74% rename from charts/async-processor/templates/ap-serviceaccounts.yaml rename to charts/llm-d-async/templates/ap-serviceaccounts.yaml index 7d266369..7639f015 100644 --- a/charts/async-processor/templates/ap-serviceaccounts.yaml +++ b/charts/llm-d-async/templates/ap-serviceaccounts.yaml @@ -1,10 +1,10 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: {{ include "async-processor.fullname" . }} + name: {{ include "llm-d-async.fullname" . }} namespace: {{ .Release.Namespace }} labels: - {{- include "async-processor.labels" . | nindent 4 }} + {{- include "llm-d-async.labels" . | nindent 4 }} {{- if .Values.ap.imagePullSecrets }} imagePullSecrets: {{- toYaml .Values.ap.imagePullSecrets | nindent 2 }} diff --git a/charts/async-processor/templates/grafana-dashboards-configmap.yaml b/charts/llm-d-async/templates/grafana-dashboards-configmap.yaml similarity index 72% rename from charts/async-processor/templates/grafana-dashboards-configmap.yaml rename to charts/llm-d-async/templates/grafana-dashboards-configmap.yaml index 35dd32d1..460fb747 100644 --- a/charts/async-processor/templates/grafana-dashboards-configmap.yaml +++ b/charts/llm-d-async/templates/grafana-dashboards-configmap.yaml @@ -2,10 +2,10 @@ apiVersion: v1 kind: ConfigMap metadata: - name: {{ include "async-processor.fullname" . }}-dashboards + name: {{ include "llm-d-async.fullname" . }}-dashboards namespace: {{ .Release.Namespace }} labels: - {{- include "async-processor.labels" . | nindent 4 }} + {{- include "llm-d-async.labels" . | nindent 4 }} {{ .Values.ap.grafana.dashboards.label }}: {{ .Values.ap.grafana.dashboards.labelValue | quote }} data: {{- (.Files.Glob "dashboards/*.json").AsConfig | nindent 2 }} diff --git a/charts/async-processor/templates/modelserver-podmonitor.yaml b/charts/llm-d-async/templates/modelserver-podmonitor.yaml similarity index 86% rename from charts/async-processor/templates/modelserver-podmonitor.yaml rename to charts/llm-d-async/templates/modelserver-podmonitor.yaml index ee0e3b7b..0d2abdef 100644 --- a/charts/async-processor/templates/modelserver-podmonitor.yaml +++ b/charts/llm-d-async/templates/modelserver-podmonitor.yaml @@ -2,9 +2,9 @@ apiVersion: monitoring.coreos.com/v1 kind: PodMonitor metadata: - name: {{ include "async-processor.fullname" . }}-modelserver + name: {{ include "llm-d-async.fullname" . }}-modelserver labels: - {{- include "async-processor.labels" . | nindent 4 }} + {{- include "llm-d-async.labels" . | nindent 4 }} spec: selector: matchLabels: diff --git a/charts/async-processor/templates/prometheus-rule.yaml b/charts/llm-d-async/templates/prometheus-rule.yaml similarity index 96% rename from charts/async-processor/templates/prometheus-rule.yaml rename to charts/llm-d-async/templates/prometheus-rule.yaml index 4282ef9e..0768e51b 100644 --- a/charts/async-processor/templates/prometheus-rule.yaml +++ b/charts/llm-d-async/templates/prometheus-rule.yaml @@ -2,16 +2,16 @@ apiVersion: monitoring.coreos.com/v1 kind: PrometheusRule metadata: - name: {{ include "async-processor.fullname" . }} + name: {{ include "llm-d-async.fullname" . }} namespace: {{ .Release.Namespace }} labels: - {{- include "async-processor.labels" . | nindent 4 }} + {{- include "llm-d-async.labels" . | nindent 4 }} {{- with .Values.ap.prometheusRule.labels }} {{- toYaml . | nindent 4 }} {{- end }} spec: groups: - - name: async-processor.rules + - name: llm-d-async.rules rules: {{- if .Values.ap.prometheusRule.rules.highRetryRate.enabled }} - alert: AsyncProcessorHighRetryRate diff --git a/charts/async-processor/templates/redis-secret.yaml b/charts/llm-d-async/templates/redis-secret.yaml similarity index 66% rename from charts/async-processor/templates/redis-secret.yaml rename to charts/llm-d-async/templates/redis-secret.yaml index d714b889..d5671c02 100644 --- a/charts/async-processor/templates/redis-secret.yaml +++ b/charts/llm-d-async/templates/redis-secret.yaml @@ -2,10 +2,10 @@ apiVersion: v1 kind: Secret metadata: - name: {{ include "async-processor.fullname" . }}-redis + name: {{ include "llm-d-async.fullname" . }}-redis namespace: {{ .Release.Namespace }} labels: - {{- include "async-processor.labels" . | nindent 4 }} + {{- include "llm-d-async.labels" . | nindent 4 }} type: Opaque stringData: url: {{ .Values.ap.redis.url | quote }} diff --git a/charts/async-processor/tests/deployment_test.yaml b/charts/llm-d-async/tests/deployment_test.yaml similarity index 95% rename from charts/async-processor/tests/deployment_test.yaml rename to charts/llm-d-async/tests/deployment_test.yaml index 8f6bbf8e..58c7467a 100644 --- a/charts/async-processor/tests/deployment_test.yaml +++ b/charts/llm-d-async/tests/deployment_test.yaml @@ -251,7 +251,7 @@ tests: path: spec.template.spec.containers[0].env content: name: OTEL_SERVICE_NAME - value: "async-processor" + value: "llm-d-async" any: true - it: should not inject OTel env vars when endpoint is empty @@ -304,19 +304,19 @@ tests: asserts: - contains: path: spec.template.spec.containers[0].args - content: --pool-config-file=/etc/async-processor/config/worker-pools.json + content: --pool-config-file=/etc/llm-d-async/config/worker-pools.json - contains: path: spec.template.spec.containers[0].volumeMounts content: name: ap-config - mountPath: /etc/async-processor/config + mountPath: /etc/llm-d-async/config readOnly: true - contains: path: spec.template.spec.volumes content: name: ap-config configMap: - name: RELEASE-NAME-async-processor-config + name: RELEASE-NAME-llm-d-async-config - it: should mount config volume and pass pubsub.topics-config-file when GCP PubSub topicsConfig is set set: @@ -329,19 +329,19 @@ tests: asserts: - contains: path: spec.template.spec.containers[0].args - content: --pubsub.topics-config-file=/etc/async-processor/config/pubsub-topics.json + content: --pubsub.topics-config-file=/etc/llm-d-async/config/pubsub-topics.json - contains: path: spec.template.spec.containers[0].volumeMounts content: name: ap-config - mountPath: /etc/async-processor/config + mountPath: /etc/llm-d-async/config readOnly: true - contains: path: spec.template.spec.volumes content: name: ap-config configMap: - name: RELEASE-NAME-async-processor-config + name: RELEASE-NAME-llm-d-async-config - it: should auto-select gcp-pubsub-gated when a topic declares a gate_type set: @@ -404,16 +404,16 @@ tests: asserts: - contains: path: spec.template.spec.containers[0].args - content: --transform-config-file=/etc/async-processor/config/transform-config.json + content: --transform-config-file=/etc/llm-d-async/config/transform-config.json - contains: path: spec.template.spec.containers[0].volumeMounts content: name: ap-config - mountPath: /etc/async-processor/config + mountPath: /etc/llm-d-async/config readOnly: true - it: should not pass transform-config-file when transformConfig is empty asserts: - notContains: path: spec.template.spec.containers[0].args - content: --transform-config-file=/etc/async-processor/config/transform-config.json + content: --transform-config-file=/etc/llm-d-async/config/transform-config.json diff --git a/charts/async-processor/tests/observability_test.yaml b/charts/llm-d-async/tests/observability_test.yaml similarity index 99% rename from charts/async-processor/tests/observability_test.yaml rename to charts/llm-d-async/tests/observability_test.yaml index 36c13c4c..97aef855 100644 --- a/charts/async-processor/tests/observability_test.yaml +++ b/charts/llm-d-async/tests/observability_test.yaml @@ -143,7 +143,7 @@ tests: path: metadata.labels.grafana_dashboard value: "1" - isNotNull: - path: data["async-processor.json"] + path: data["llm-d-async.json"] - it: should use custom Grafana dashboard label templates: diff --git a/charts/async-processor/values.yaml b/charts/llm-d-async/values.yaml similarity index 99% rename from charts/async-processor/values.yaml rename to charts/llm-d-async/values.yaml index 54e5284d..014601b5 100644 --- a/charts/async-processor/values.yaml +++ b/charts/llm-d-async/values.yaml @@ -177,7 +177,7 @@ ap: # address: "redis:6379" # limit: "5" queuesConfig: [] - # PodMonitor for async-processor metrics scraping. + # PodMonitor for llm-d-async metrics scraping. podMonitor: enabled: false interval: "30s" diff --git a/deploy/install.sh b/deploy/install.sh index 37924bd0..341e4980 100755 --- a/deploy/install.sh +++ b/deploy/install.sh @@ -31,15 +31,15 @@ REDIS_NS=${REDIS_NS:-"redis"} PROMETHEUS_SECRET_NS=${PROMETHEUS_SECRET_NS:-$MONITORING_NAMESPACE} # AP Configuration -AP_IMAGE_REPO=${AP_IMAGE_REPO:-"ghcr.io/llm-d/async-processor"} +AP_IMAGE_REPO=${AP_IMAGE_REPO:-"ghcr.io/llm-d/llm-d-async"} AP_IMAGE_TAG=${AP_IMAGE_TAG:-"latest"} AP_IMAGE_PULL_POLICY=${AP_IMAGE_PULL_POLICY:-"Never"} -AP_RELEASE_NAME=${AP_RELEASE_NAME:-"async-processor"} +AP_RELEASE_NAME=${AP_RELEASE_NAME:-"llm-d-async"} VLLM_SVC_ENABLED=${VLLM_SVC_ENABLED:-true} VLLM_SVC_NODEPORT=${VLLM_SVC_NODEPORT:-30000} SKIP_TLS_VERIFY=${SKIP_TLS_VERIFY:-"false"} AP_LOG_LEVEL=${AP_LOG_LEVEL:-"info"} -VALUES_FILE=${VALUES_FILE:-"$AP_PROJECT/charts/async-processor/values.yaml"} +VALUES_FILE=${VALUES_FILE:-"$AP_PROJECT/charts/llm-d-async/values.yaml"} # Redis / Valkey Configuration # REDIS_CHART selects the Helm chart for the message queue backend. @@ -149,7 +149,7 @@ Examples: $(basename "$0") # Deploy with custom AP image - IMG=/async-processor:tag $(basename "$0") + IMG=/llm-d-async:tag $(basename "$0") # Deploy with custom model and accelerator $(basename "$0") -m unsloth/Meta-Llama-3.1-8B -a A100 @@ -403,7 +403,7 @@ deploy_ap_controller() { # Deploy AP using Helm chart log_info "Installing Async-Processor via Helm chart" - helm upgrade -i "$AP_RELEASE_NAME" ${AP_PROJECT}/charts/async-processor \ + helm upgrade -i "$AP_RELEASE_NAME" ${AP_PROJECT}/charts/llm-d-async \ -n $AP_NS \ --values $VALUES_FILE \ --set ap.image.repository=$AP_IMAGE_REPO \ @@ -418,7 +418,7 @@ deploy_ap_controller() { # Wait for AP to be ready log_info "Waiting for AP to be ready..." - kubectl wait --for=condition=Ready pod -l app.kubernetes.io/name=async-processor -n $AP_NS --timeout=30s || \ + kubectl wait --for=condition=Ready pod -l app.kubernetes.io/name=llm-d-async -n $AP_NS --timeout=30s || \ log_warning "AP is not ready yet - check 'kubectl get pods -n $AP_NS'" log_success "AP deployment complete" @@ -438,7 +438,7 @@ deploy_redis() { *) svc="${REDIS_RELEASE_NAME}-master" ;; esac - # Create a secret with the Redis-protocol URL in the AP namespace so the async-processor can connect + # Create a secret with the Redis-protocol URL in the AP namespace so the llm-d-async can connect local redis_url="redis://${svc}.${REDIS_NS}.svc.cluster.local:6379" log_info "Creating message queue URL secret in $AP_NS namespace" kubectl create secret generic redis-creds \ @@ -544,7 +544,7 @@ verify_deployment() { # Check AP pods log_info "Checking AP pods..." sleep 10 - if kubectl get pods -n $AP_NS -l app.kubernetes.io/name=async-processor 2>/dev/null | grep -q Running; then + if kubectl get pods -n $AP_NS -l app.kubernetes.io/name=llm-d-async 2>/dev/null | grep -q Running; then log_success "AP is running" else log_warning "AP may still be starting" @@ -633,7 +633,7 @@ print_summary() { echo " kubectl describe variantautoscaling $LLM_D_MODELSERVICE_NAME-decode -n $LLMD_NS" echo "" echo "3. View AP logs:" - echo " kubectl logs -n $AP_NS -l app.kubernetes.io/name=async-processor -f" + echo " kubectl logs -n $AP_NS -l app.kubernetes.io/name=llm-d-async -f" echo "" echo "4. Check external metrics API:" echo " kubectl get --raw \"/apis/external.metrics.k8s.io/v1beta1/namespaces/$LLMD_NS/inferno_desired_replicas\" | jq" @@ -658,7 +658,7 @@ print_summary() { echo "================" echo "" echo "• Check AP controller logs:" - echo " kubectl logs -n $AP_NS -l app.kubernetes.io/name=async-processor" + echo " kubectl logs -n $AP_NS -l app.kubernetes.io/name=llm-d-async" echo "" echo "• Check all pods in llm-d namespace:" echo " kubectl get pods -n $LLMD_NS" diff --git a/docs/guides/e2e-deploy.md b/docs/guides/e2e-deploy.md index 51581d18..94fae565 100644 --- a/docs/guides/e2e-deploy.md +++ b/docs/guides/e2e-deploy.md @@ -1,6 +1,6 @@ # E2E Deploy: Async Processor with Dispatch Budget Gate -Deploy async-processor with a `prometheus-budget` gate on a real Kubernetes cluster, +Deploy llm-d-async with a `prometheus-budget` gate on a real Kubernetes cluster, backed by a real vLLM model server and the upstream llm-d stack. ## Prerequisites @@ -136,12 +136,12 @@ helm install redis bitnami/valkey -n redis --create-namespace --set auth.enabled ## Step 9: Deploy Async Processor with dispatch budget gate ```bash -helm install async-processor ${ASYNC_REPO}/charts/async-processor/ \ - -f ${ASYNC_REPO}/docs/guides/e2e-deploy/async-processor-values.yaml \ +helm install llm-d-async ${ASYNC_REPO}/charts/llm-d-async/ \ + -f ${ASYNC_REPO}/docs/guides/e2e-deploy/llm-d-async-values.yaml \ -n ${NAMESPACE} ``` -The values file (`docs/guides/e2e-deploy/async-processor-values.yaml`) configures: +The values file (`docs/guides/e2e-deploy/llm-d-async-values.yaml`) configures: - Image: `ghcr.io/llm-d/llm-d-async:938cd44` - Queue: Redis sorted-set with `redis.url` set directly (chart creates the Secret), configured via `queuesConfig` - Gate: `prometheus-budget` with pool=`optimized-baseline`, max_concurrency=100, baseline=0.05 (per-queue) @@ -161,7 +161,7 @@ The values file (`docs/guides/e2e-deploy/async-processor-values.yaml`) configure > ``` - `modelServerMonitor.enabled: true` — creates a PodMonitor that relabels the `inference_pool` pod label into vLLM metrics (required for the dispatch budget gate fallback) -- `podMonitor.enabled: true` — creates a PodMonitor that scrapes the async-processor's own +- `podMonitor.enabled: true` — creates a PodMonitor that scrapes the llm-d-async's own Prometheus metrics (retry rate, success rate, latency, etc.) - `prometheusRule.enabled: true` — installs alert rules for high retry rate, deadline exceeded rate, low success rate, and high shed rate @@ -178,7 +178,7 @@ kubectl get pods -n ${NAMESPACE} # Async processor logs should show "using fallback metric source" (vLLM saturation), # NOT "all metric sources unavailable" -kubectl logs -n ${NAMESPACE} -l app.kubernetes.io/name=async-processor --tail=10 +kubectl logs -n ${NAMESPACE} -l app.kubernetes.io/name=llm-d-async --tail=10 ``` ### Verify metrics pipeline @@ -209,10 +209,10 @@ kubectl run --rm -i prom-budget --image=curlimages/curl --restart=Never -n ${NAM # Expected: value = 1 ``` -### Verify async-processor monitoring +### Verify llm-d-async monitoring Once you have sent at least one async request (see next section), verify that the -async-processor's own metrics are being scraped and that alerts/dashboards are available: +llm-d-async's own metrics are being scraped and that alerts/dashboards are available: ```bash # Async-processor metrics in Prometheus (should show request counters) @@ -223,11 +223,11 @@ kubectl run --rm -i prom-ap --image=curlimages/curl --restart=Never -n ${NAMESPA # Verify PrometheusRule is installed kubectl get prometheusrules -n ${NAMESPACE} -# Expected: async-processor rule listed +# Expected: llm-d-async rule listed # Verify Grafana dashboard ConfigMap is present kubectl get configmap -n ${NAMESPACE} -l grafana_dashboard=1 -# Expected: async-processor-dashboards ConfigMap listed +# Expected: llm-d-async-dashboards ConfigMap listed ``` ### Test async request end-to-end @@ -267,7 +267,7 @@ kubectl run --rm -i test-queued --image=redis --restart=Never -n ${NAMESPACE} -- # Expected: 1 # Async processor logs should show: "using fallback value" {"fallback": 0, "error": "invalid metric value: NaN"} -kubectl logs -n ${NAMESPACE} -l app.kubernetes.io/name=async-processor --tail=5 +kubectl logs -n ${NAMESPACE} -l app.kubernetes.io/name=llm-d-async --tail=5 # Scale back up — gate opens, queued request gets dispatched kubectl scale deployment vllm-qwen3-0-6b-decode -n ${NAMESPACE} --replicas=1 @@ -391,7 +391,7 @@ kubectl run --rm -i check-drained --image=redis --restart=Never -n ${NAMESPACE} ```bash kubectl delete job hey-loadtest guidellm-loadtest -n ${NAMESPACE} --ignore-not-found -helm uninstall async-processor -n ${NAMESPACE} +helm uninstall llm-d-async -n ${NAMESPACE} helm uninstall redis -n redis kubectl delete -n ${NAMESPACE} -k ${ASYNC_REPO}/docs/guides/e2e-deploy/modelserver/ helm uninstall ${GUIDE_NAME} -n ${NAMESPACE} diff --git a/docs/guides/e2e-deploy/async-processor-values.yaml b/docs/guides/e2e-deploy/llm-d-async-values.yaml similarity index 100% rename from docs/guides/e2e-deploy/async-processor-values.yaml rename to docs/guides/e2e-deploy/llm-d-async-values.yaml diff --git a/scripts/publish-helm-chart.sh b/scripts/publish-helm-chart.sh index c9389f45..c6ae800c 100755 --- a/scripts/publish-helm-chart.sh +++ b/scripts/publish-helm-chart.sh @@ -36,19 +36,19 @@ command -v helm >/dev/null 2>&1 || { exit 1 } -yq -i '.ap.image.tag = strenv(VERSION)' charts/async-processor/values.yaml +yq -i '.ap.image.tag = strenv(VERSION)' charts/llm-d-async/values.yaml # Chart version must be bare SemVer (OCI/Helm requirement), so it uses the # v-stripped CHART_VERSION. appVersion keeps the leading "v" to match the # published image tag (images are tagged with the git tag verbatim, e.g. # v0.7.1). This makes the recommended empty `ap.image.tag` default — which # falls back to .Chart.AppVersion — resolve to a tag that actually exists. -yq -i '.version = strenv(CHART_VERSION) | .appVersion = strenv(VERSION)' charts/async-processor/Chart.yaml +yq -i '.version = strenv(CHART_VERSION) | .appVersion = strenv(VERSION)' charts/llm-d-async/Chart.yaml -helm package charts/async-processor -d release/ +helm package charts/llm-d-async -d release/ -(cd release && sha256sum "async-processor-${CHART_VERSION}.tgz" >> SHA256SUMS && cat SHA256SUMS) +(cd release && sha256sum "llm-d-async-${CHART_VERSION}.tgz" >> SHA256SUMS && cat SHA256SUMS) printf '%s' "${GITHUB_TOKEN}" | helm registry login ghcr.io -u "${GITHUB_ACTOR}" --password-stdin -helm push "release/async-processor-${CHART_VERSION}.tgz" "${HELM_OCI_REGISTRY}" +helm push "release/llm-d-async-${CHART_VERSION}.tgz" "${HELM_OCI_REGISTRY}" -echo "Helm chart published: ${HELM_OCI_REGISTRY}/async-processor:${CHART_VERSION}" +echo "Helm chart published: ${HELM_OCI_REGISTRY}/llm-d-async:${CHART_VERSION}" diff --git a/test/e2e/e2e_endpoint_scrape_gate_test.go b/test/e2e/e2e_endpoint_scrape_gate_test.go index 6b97f275..b95346e6 100644 --- a/test/e2e/e2e_endpoint_scrape_gate_test.go +++ b/test/e2e/e2e_endpoint_scrape_gate_test.go @@ -13,7 +13,7 @@ import ( // endpoint directly (no Prometheus or EPP needed): // // setSimWaitingRequests → sim reports vllm:num_requests_waiting on /metrics -// → async-processor scrapes /metrics, computes saturation, gate opens/closes +// → llm-d-async scrapes /metrics, computes saturation, gate opens/closes var _ = ginkgo.Describe("Endpoint Scrape Dispatch Gate E2E", ginkgo.Ordered, func() { var ctx context.Context diff --git a/test/e2e/e2e_health_test.go b/test/e2e/e2e_health_test.go index babdb2ce..221ef687 100644 --- a/test/e2e/e2e_health_test.go +++ b/test/e2e/e2e_health_test.go @@ -22,7 +22,7 @@ var _ = ginkgo.Describe("Health Probes", func() { cmd := exec.Command("kubectl", "--kubeconfig", kindKubeconfig, "-n", nsName, "port-forward", - "deployment/integration-async-processor", + "deployment/integration-llm-d-async", fmt.Sprintf("%d:8081", localPort)) session, err := gexec.Start(cmd, ginkgo.GinkgoWriter, ginkgo.GinkgoWriter) gomega.Expect(err).NotTo(gomega.HaveOccurred()) diff --git a/test/e2e/e2e_multitenant_test.go b/test/e2e/e2e_multitenant_test.go index d6db5bf7..6b9b3a55 100644 --- a/test/e2e/e2e_multitenant_test.go +++ b/test/e2e/e2e_multitenant_test.go @@ -82,7 +82,7 @@ var _ = ginkgo.Describe("Multi-tenant Quota and Priority E2E", ginkgo.Ordered, f ginkgo.It("parks the batch pool under saturation and resumes when it clears", func() { // Batch is the least saturation-tolerant tier: its wait-on-refuse pool gate // parks the workers (ActionWait) under load, while premium (no pool gate) is - // unthrottled at the async-processor. Note: the e2e drives saturation via the + // unthrottled at the llm-d-async. Note: the e2e drives saturation via the // EPP admission metric, which also throttles real gateway traffic, so this // spec asserts the batch pool-gate mechanism directly (premium-vs-batch // prioritization is covered at the quota level above, with saturation low). diff --git a/test/e2e/e2e_saturation_gate_test.go b/test/e2e/e2e_saturation_gate_test.go index 33fa0798..a9ae5bec 100644 --- a/test/e2e/e2e_saturation_gate_test.go +++ b/test/e2e/e2e_saturation_gate_test.go @@ -15,7 +15,7 @@ import ( // → EPP scrapes sim, computes inference_extension_flow_control_pool_saturation // (saturation = Max(WaitingQueue/QueueDepthThreshold, KVCache/KVCacheThreshold)) // → Prometheus scrapes EPP -// → async-processor queries Prometheus, gate opens/closes +// → llm-d-async queries Prometheus, gate opens/closes // // Probe requests through Envoy → EPP are needed to trigger the flow control // admission layer that records the saturation metric. diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index c5cc2ed3..6eec8be9 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -42,8 +42,8 @@ const ( prometheusManifest = "./yaml/prometheus.yaml" jaegerManifest = "./yaml/jaeger.yaml" - // Helm chart and per-instance values for async-processor deployments. - chartPath = "../../charts/async-processor" + // Helm chart and per-instance values for llm-d-async deployments. + chartPath = "../../charts/llm-d-async" helmValuesDir = "./helm" ) @@ -56,7 +56,7 @@ var ( jaegerPort string = env.GetEnvString("E2E_INTEGRATION_JAEGER_PORT", "30494", ginkgo.GinkgoLogr) containerRuntime = detectContainerRuntime() - apImage = env.GetEnvString("AP_IMAGE", "ghcr.io/llm-d/async-processor:e2e-test", ginkgo.GinkgoLogr) + apImage = env.GetEnvString("AP_IMAGE", "ghcr.io/llm-d/llm-d-async:e2e-test", ginkgo.GinkgoLogr) eppImage = env.GetEnvString("EPP_IMAGE", "registry.k8s.io/gateway-api-inference-extension/epp:v1.5.0", ginkgo.GinkgoLogr) simImage = env.GetEnvString("SIM_IMAGE", "ghcr.io/llm-d/llm-d-inference-sim:v0.10.0", ginkgo.GinkgoLogr) redisImage = env.GetEnvString("REDIS_IMAGE", "valkey/valkey:8-alpine", ginkgo.GinkgoLogr) @@ -157,7 +157,7 @@ func setupK8sCluster() { checkCmd := exec.Command("kind", "get", "clusters") output, err := checkCmd.Output() if err == nil && strings.Contains(string(output), kindClusterName) { - ginkgo.By("Kind cluster " + kindClusterName + " already exists, rebuilding and loading async-processor image") + ginkgo.By("Kind cluster " + kindClusterName + " already exists, rebuilding and loading llm-d-async image") command := exec.Command(containerRuntime, "build", "-t", apImage, projectRoot()) session, err := gexec.Start(command, ginkgo.GinkgoWriter, ginkgo.GinkgoWriter) gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) @@ -193,7 +193,7 @@ func setupK8sCluster() { gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) gomega.Eventually(session).WithTimeout(600 * time.Second).Should(gexec.Exit(0)) - ginkgo.By("Building async-processor image") + ginkgo.By("Building llm-d-async image") command = exec.Command(containerRuntime, "build", "-t", apImage, projectRoot()) session, err = gexec.Start(command, ginkgo.GinkgoWriter, ginkgo.GinkgoWriter) gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) @@ -346,7 +346,7 @@ func applyManifests() { // Patch Envoy service to NodePort so test code can reach it for probe requests. kubectlPatchEnvoyNodePort() - ginkgo.By("Installing async-processor helm releases") + ginkgo.By("Installing llm-d-async helm releases") imageRepo, imageTag := splitImage(apImage) for _, r := range []struct{ name, values string }{ {"integration", helmValuesDir + "/integration.yaml"}, @@ -581,20 +581,20 @@ func doRedeployEPPWithFlowControl() { // Envoy/EPP. Without this, processors that poll during the EPP restart // window can get 404s from Envoy that are classified as non-retryable. for _, deploy := range []string{ - "integration-async-processor", - "saturation-async-processor", - "budget-async-processor", - "redis-gate-async-processor", - "quota-async-processor", - "composite-async-processor", - "prometheus-query-async-processor", - "endpoint-scrape-async-processor", - "short-drain-async-processor", - "multitenant-async-processor", - "tier-priority-async-processor", - "mt-merge-async-processor", - "benchmark-async-processor", - "benchmark-pool-gate-async-processor", + "integration-llm-d-async", + "saturation-llm-d-async", + "budget-llm-d-async", + "redis-gate-llm-d-async", + "quota-llm-d-async", + "composite-llm-d-async", + "prometheus-query-llm-d-async", + "endpoint-scrape-llm-d-async", + "short-drain-llm-d-async", + "multitenant-llm-d-async", + "tier-priority-llm-d-async", + "mt-merge-llm-d-async", + "benchmark-llm-d-async", + "benchmark-pool-gate-llm-d-async", } { cmd := exec.Command("kubectl", "--kubeconfig", kindKubeconfig, "-n", nsName, "rollout", "restart", "deployment/"+deploy) @@ -603,20 +603,20 @@ func doRedeployEPPWithFlowControl() { gomega.Eventually(s).WithTimeout(30 * time.Second).Should(gexec.Exit(0)) } for _, deploy := range []string{ - "integration-async-processor", - "saturation-async-processor", - "budget-async-processor", - "redis-gate-async-processor", - "quota-async-processor", - "composite-async-processor", - "prometheus-query-async-processor", - "endpoint-scrape-async-processor", - "short-drain-async-processor", - "multitenant-async-processor", - "tier-priority-async-processor", - "mt-merge-async-processor", - "benchmark-async-processor", - "benchmark-pool-gate-async-processor", + "integration-llm-d-async", + "saturation-llm-d-async", + "budget-llm-d-async", + "redis-gate-llm-d-async", + "quota-llm-d-async", + "composite-llm-d-async", + "prometheus-query-llm-d-async", + "endpoint-scrape-llm-d-async", + "short-drain-llm-d-async", + "multitenant-llm-d-async", + "tier-priority-llm-d-async", + "mt-merge-llm-d-async", + "benchmark-llm-d-async", + "benchmark-pool-gate-llm-d-async", } { cmd := exec.Command("kubectl", "--kubeconfig", kindKubeconfig, "-n", nsName, "rollout", "status", "deployment/"+deploy, "--timeout=120s") diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index ef861cc7..82c0b4e3 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -175,7 +175,7 @@ var _ = ginkgo.Describe("General Integration", func() { ginkgo.DeferCleanup(func() { setEnvoyFaultDelay(envoyAdminURL, 0) cmd := exec.Command("kubectl", "--kubeconfig", kindKubeconfig, - "-n", nsName, "scale", "deployment/integration-async-processor", + "-n", nsName, "scale", "deployment/integration-llm-d-async", "--replicas=1", "--timeout=60s") session, err := gexec.Start(cmd, ginkgo.GinkgoWriter, ginkgo.GinkgoWriter) gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) @@ -183,7 +183,7 @@ var _ = ginkgo.Describe("General Integration", func() { cmd = exec.Command("kubectl", "--kubeconfig", kindKubeconfig, "-n", nsName, "rollout", "status", - "deployment/integration-async-processor", "--timeout=120s") + "deployment/integration-llm-d-async", "--timeout=120s") session, err = gexec.Start(cmd, ginkgo.GinkgoWriter, ginkgo.GinkgoWriter) gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) gomega.Eventually(session).WithTimeout(180 * time.Second).Should(gexec.Exit(0)) @@ -202,7 +202,7 @@ var _ = ginkgo.Describe("General Integration", func() { // Scale the deployment to 0 to trigger graceful shutdown. cmd := exec.Command("kubectl", "--kubeconfig", kindKubeconfig, - "-n", nsName, "scale", "deployment/integration-async-processor", + "-n", nsName, "scale", "deployment/integration-llm-d-async", "--replicas=0", "--timeout=60s") session, err := gexec.Start(cmd, ginkgo.GinkgoWriter, ginkgo.GinkgoWriter) gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) @@ -240,7 +240,7 @@ var _ = ginkgo.Describe("General Integration", func() { ginkgo.DeferCleanup(func() { setEnvoyFaultDelay(envoyAdminURL, 0) cmd := exec.Command("kubectl", "--kubeconfig", kindKubeconfig, - "-n", nsName, "scale", "deployment/short-drain-async-processor", + "-n", nsName, "scale", "deployment/short-drain-llm-d-async", "--replicas=1", "--timeout=60s") session, err := gexec.Start(cmd, ginkgo.GinkgoWriter, ginkgo.GinkgoWriter) gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) @@ -248,7 +248,7 @@ var _ = ginkgo.Describe("General Integration", func() { cmd = exec.Command("kubectl", "--kubeconfig", kindKubeconfig, "-n", nsName, "rollout", "status", - "deployment/short-drain-async-processor", "--timeout=120s") + "deployment/short-drain-llm-d-async", "--timeout=120s") session, err = gexec.Start(cmd, ginkgo.GinkgoWriter, ginkgo.GinkgoWriter) gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) gomega.Eventually(session).WithTimeout(180 * time.Second).Should(gexec.Exit(0)) @@ -266,7 +266,7 @@ var _ = ginkgo.Describe("General Integration", func() { // Scale to 0 to trigger graceful shutdown. cmd := exec.Command("kubectl", "--kubeconfig", kindKubeconfig, - "-n", nsName, "scale", "deployment/short-drain-async-processor", + "-n", nsName, "scale", "deployment/short-drain-llm-d-async", "--replicas=0", "--timeout=60s") session, err := gexec.Start(cmd, ginkgo.GinkgoWriter, ginkgo.GinkgoWriter) gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) @@ -319,7 +319,7 @@ var _ = ginkgo.Describe("General Integration", func() { // Wait for the replacement pod to be ready. cmd = exec.Command("kubectl", "--kubeconfig", kindKubeconfig, "-n", nsName, "rollout", "status", - "deployment/integration-async-processor", "--timeout=120s") + "deployment/integration-llm-d-async", "--timeout=120s") session, err = gexec.Start(cmd, ginkgo.GinkgoWriter, ginkgo.GinkgoWriter) gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) gomega.Eventually(session).WithTimeout(180 * time.Second).Should(gexec.Exit(0)) diff --git a/test/e2e/utils_test.go b/test/e2e/utils_test.go index 20587444..930fd2d4 100644 --- a/test/e2e/utils_test.go +++ b/test/e2e/utils_test.go @@ -189,7 +189,7 @@ func queryProm(promURL, query string) float64 { // budgetPromQL is the same PromQL the budget gate's primary source uses // (EPP flow control queue_size). The multiplier (* 1) is max_concurrency and -// must match the budget processor's gate-params in async-processor.yaml. +// must match the budget processor's gate-params in llm-d-async.yaml. // We use max_concurrency=1 so a single queued request in EPP's admission // layer is enough to drive the budget to zero. const budgetPromQL = `1 - (sum by(inference_pool)(inference_extension_flow_control_queue_size{inference_pool="e2e-pool"}) / on() (inference_pool_ready_pods{name="e2e-pool"} * 1))` diff --git a/test/e2e/yaml/async-processor-composite.yaml b/test/e2e/yaml/llm-d-async-composite.yaml similarity index 83% rename from test/e2e/yaml/async-processor-composite.yaml rename to test/e2e/yaml/llm-d-async-composite.yaml index ab63a503..9afae279 100644 --- a/test/e2e/yaml/async-processor-composite.yaml +++ b/test/e2e/yaml/llm-d-async-composite.yaml @@ -1,23 +1,23 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: async-processor-composite + name: llm-d-async-composite namespace: e2e-test labels: - app: async-processor-composite + app: llm-d-async-composite spec: replicas: 1 selector: matchLabels: - app: async-processor-composite + app: llm-d-async-composite template: metadata: labels: - app: async-processor-composite + app: llm-d-async-composite spec: - serviceAccountName: async-processor + serviceAccountName: llm-d-async containers: - - name: async-processor + - name: llm-d-async image: ${AP_IMAGE} imagePullPolicy: IfNotPresent args: diff --git a/test/e2e/yaml/async-processor-quota.yaml b/test/e2e/yaml/llm-d-async-quota.yaml similarity index 78% rename from test/e2e/yaml/async-processor-quota.yaml rename to test/e2e/yaml/llm-d-async-quota.yaml index 00a5e487..6c6c9b7b 100644 --- a/test/e2e/yaml/async-processor-quota.yaml +++ b/test/e2e/yaml/llm-d-async-quota.yaml @@ -1,23 +1,23 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: async-processor-quota + name: llm-d-async-quota namespace: e2e-test labels: - app: async-processor-quota + app: llm-d-async-quota spec: replicas: 1 selector: matchLabels: - app: async-processor-quota + app: llm-d-async-quota template: metadata: labels: - app: async-processor-quota + app: llm-d-async-quota spec: - serviceAccountName: async-processor + serviceAccountName: llm-d-async containers: - - name: async-processor + - name: llm-d-async image: ${AP_IMAGE} imagePullPolicy: IfNotPresent args: From 8008282498a1f668b637f818d43971df0904f2b1 Mon Sep 17 00:00:00 2001 From: Shimi Bandiel Date: Tue, 21 Jul 2026 21:28:25 +0000 Subject: [PATCH 2/5] docs(release-notes): add breaking fragment for #340 (chart rename) Signed-off-by: Shimi Bandiel --- release-notes.d/unreleased/340.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 release-notes.d/unreleased/340.md diff --git a/release-notes.d/unreleased/340.md b/release-notes.d/unreleased/340.md new file mode 100644 index 00000000..39ae4346 --- /dev/null +++ b/release-notes.d/unreleased/340.md @@ -0,0 +1,7 @@ +--- +pr: 340 +url: https://github.com/llm-d/llm-d-async/pull/340 +author: shimib +date: 2026-07-21 +--- +Breaking: the Helm chart is renamed from `async-processor` to `llm-d-async` and now publishes to `oci://ghcr.io/llm-d/charts/llm-d-async`. Rendered resources are named `llm-d-async-*` with `app.kubernetes.io/name=llm-d-async`; because a Deployment's selector is immutable, upgrading from an existing `async-processor` install requires uninstall + reinstall. The old `charts/async-processor` package remains available (no redirect) but receives no new releases. From 25d245a79e83ee3f0cd2ec7de3471870bb9cef0d Mon Sep 17 00:00:00 2001 From: Shimi Bandiel Date: Tue, 21 Jul 2026 21:58:57 +0000 Subject: [PATCH 3/5] fix(chart): keep container command as /async-processor (binary name) The chart rename changed the container command to /llm-d-async, but the image's binary is still async-processor (Dockerfile ENTRYPOINT), so the pod would fail to start. Revert just the command path to match the (intentionally unchanged) binary name. Signed-off-by: Shimi Bandiel --- charts/llm-d-async/templates/ap-deployments.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/charts/llm-d-async/templates/ap-deployments.yaml b/charts/llm-d-async/templates/ap-deployments.yaml index 222796b4..836f1de7 100644 --- a/charts/llm-d-async/templates/ap-deployments.yaml +++ b/charts/llm-d-async/templates/ap-deployments.yaml @@ -40,7 +40,9 @@ spec: type: RuntimeDefault containers: - command: - - /llm-d-async + # Must match the binary produced by the Dockerfile (ENTRYPOINT /async-processor); + # the binary name is intentionally not renamed by the chart rename. + - /async-processor args: {{- if .Values.ap.redis.enabled }} {{- if eq (.Values.ap.messageQueueImpl | default "redis-pubsub") "redis-sortedset" }} From 9c2d42db6390925df2d854217d7e53fc21acbeee Mon Sep 17 00:00:00 2001 From: Shimi Bandiel Date: Tue, 21 Jul 2026 22:08:43 +0000 Subject: [PATCH 4/5] feat: rename binary + OTEL service name to llm-d-async Follow-up within #340 (per review) to complete the identity rename so the container command can be /llm-d-async: - Dockerfile: build/COPY/ENTRYPOINT the binary as llm-d-async; chart container command -> /llm-d-async (matches the image). - OTEL: default service name -> llm-d-async, aligning otel.go with its own doc comment AND the chart's OTEL_SERVICE_NAME env (already llm-d-async on this branch). Update the e2e Jaeger query and README default to match, so the e2e queries traces under the correct service name. - Makefile IMG/E2E_IMG defaults -> ghcr.io/llm-d/llm-d-async (they pointed at the nonexistent async-processor image). Namespaces (*-system/-monitoring) and the buildx builder name are left as-is (infra identifiers, not the app/image name). Signed-off-by: Shimi Bandiel --- Dockerfile | 6 +++--- Makefile | 6 +++--- README.md | 2 +- charts/llm-d-async/templates/ap-deployments.yaml | 5 ++--- internal/otel/otel.go | 2 +- test/e2e/e2e_otel_test.go | 6 +++--- 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9cea421b..07f09d30 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,13 +26,13 @@ COPY internal/ internal # was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO # the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, # by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. -RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -ldflags "${LDFLAGS}" -o async-processor cmd/main.go +RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -ldflags "${LDFLAGS}" -o llm-d-async cmd/main.go # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details FROM gcr.io/distroless/static:nonroot WORKDIR / -COPY --from=builder /workspace/async-processor . +COPY --from=builder /workspace/llm-d-async . USER 65532:65532 -ENTRYPOINT ["/async-processor"] +ENTRYPOINT ["/llm-d-async"] diff --git a/Makefile b/Makefile index ad578e9e..6f168b5f 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ export # Image URL to use all building/pushing image targets IMAGE_TAG_BASE ?= ghcr.io/llm-d IMG_TAG ?= latest -IMG ?= $(IMAGE_TAG_BASE)/async-processor:$(IMG_TAG) +IMG ?= $(IMAGE_TAG_BASE)/llm-d-async:$(IMG_TAG) # Versioning information VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev") @@ -160,7 +160,7 @@ undeploy-ap-on-k8s: # Optional env vars: # GAIE_ROOT — GAIE checkout; enables local EPP build and CRDs # SIM_ROOT — llm-d-inference-sim checkout; enables local sim build -# AP_IMAGE — async-processor image tag (default: $(IMAGE_TAG_BASE)/async-processor:e2e-test) +# AP_IMAGE — async-processor image tag (default: $(IMAGE_TAG_BASE)/llm-d-async:e2e-test) # EPP_IMAGE — EPP image tag (default: registry.k8s.io/.../epp:v1.5.0) # SIM_IMAGE — inference-sim image tag (default: ghcr.io/llm-d/llm-d-inference-sim:v0.0.0-test) # REDIS_IMAGE — Redis/Valkey image for E2E MQ (default: valkey/valkey:8-alpine) @@ -171,7 +171,7 @@ undeploy-ap-on-k8s: # E2E_INTEGRATION_REDIS_PORT, E2E_INTEGRATION_PROM_PORT, # E2E_INTEGRATION_SIM_PORT, E2E_INTEGRATION_ENVOY_PORT, # E2E_INTEGRATION_ENVOY_ADMIN_PORT -E2E_IMG ?= $(IMAGE_TAG_BASE)/async-processor:e2e-test +E2E_IMG ?= $(IMAGE_TAG_BASE)/llm-d-async:e2e-test .PHONY: test-e2e test-e2e: ## Run e2e integration tests against a Kind cluster diff --git a/README.md b/README.md index 724ee22a..4ddac994 100644 --- a/README.md +++ b/README.md @@ -498,7 +498,7 @@ Tracing is controlled via standard OpenTelemetry environment variables. Set `OTE |---------------------|-------------|---------| | `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP gRPC collector endpoint (e.g., `http://jaeger:4317`). Empty disables tracing. | _(disabled)_ | | `OTEL_EXPORTER_OTLP_INSECURE` | Use plaintext gRPC connection | `true` | -| `OTEL_SERVICE_NAME` | Service name for traces | `async-processor` | +| `OTEL_SERVICE_NAME` | Service name for traces | `llm-d-async` | | `OTEL_TRACES_SAMPLER` | Sampling strategy (`always_on`, `parentbased_traceidratio`, etc.) | `parentbased_traceidratio` | | `OTEL_TRACES_SAMPLER_ARG` | Sampling ratio (0.0–1.0) | `1.0` | diff --git a/charts/llm-d-async/templates/ap-deployments.yaml b/charts/llm-d-async/templates/ap-deployments.yaml index 836f1de7..5824ee63 100644 --- a/charts/llm-d-async/templates/ap-deployments.yaml +++ b/charts/llm-d-async/templates/ap-deployments.yaml @@ -40,9 +40,8 @@ spec: type: RuntimeDefault containers: - command: - # Must match the binary produced by the Dockerfile (ENTRYPOINT /async-processor); - # the binary name is intentionally not renamed by the chart rename. - - /async-processor + # Must match the binary produced by the Dockerfile (ENTRYPOINT /llm-d-async). + - /llm-d-async args: {{- if .Values.ap.redis.enabled }} {{- if eq (.Values.ap.messageQueueImpl | default "redis-pubsub") "redis-sortedset" }} diff --git a/internal/otel/otel.go b/internal/otel/otel.go index 5bed7e5d..8cf9387f 100644 --- a/internal/otel/otel.go +++ b/internal/otel/otel.go @@ -32,7 +32,7 @@ import ( "github.com/go-logr/logr" ) -const defaultServiceName = "async-processor" +const defaultServiceName = "llm-d-async" const ( AttrRequestID = "request.id" diff --git a/test/e2e/e2e_otel_test.go b/test/e2e/e2e_otel_test.go index c00dbeb5..a62d0670 100644 --- a/test/e2e/e2e_otel_test.go +++ b/test/e2e/e2e_otel_test.go @@ -100,8 +100,8 @@ var _ = ginkgo.Describe("OpenTelemetry tracing", ginkgo.Ordered, func() { popResult(ctx, rdb, integrationResultQueue) - // Poll Jaeger for traces from async-processor instead of a fixed sleep - jaegerQueryURL := jaegerURL + "/api/traces?service=async-processor&limit=5&lookback=1m" + // Poll Jaeger for traces from llm-d-async instead of a fixed sleep + jaegerQueryURL := jaegerURL + "/api/traces?service=llm-d-async&limit=5&lookback=1m" gomega.Eventually(func(g gomega.Gomega) { resp, err := jaegerClient.Get(jaegerQueryURL) g.Expect(err).NotTo(gomega.HaveOccurred()) @@ -116,7 +116,7 @@ var _ = ginkgo.Describe("OpenTelemetry tracing", ginkgo.Ordered, func() { } g.Expect(json.Unmarshal(body, &result)).To(gomega.Succeed()) g.Expect(result.Data).NotTo(gomega.BeEmpty(), - "no traces from async-processor in Jaeger yet") + "no traces from llm-d-async in Jaeger yet") }, 30*time.Second, 2*time.Second).Should(gomega.Succeed()) ginkgo.GinkgoLogr.Info("OTel export verified (no producer context)") From 81a279c8706d7b938a8b09702e698d98be3522df Mon Sep 17 00:00:00 2001 From: Shimi Bandiel Date: Tue, 21 Jul 2026 22:09:44 +0000 Subject: [PATCH 5/5] docs(release-notes): note the OTEL service-name change in #340 fragment Signed-off-by: Shimi Bandiel --- release-notes.d/unreleased/340.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-notes.d/unreleased/340.md b/release-notes.d/unreleased/340.md index 39ae4346..37c9ea89 100644 --- a/release-notes.d/unreleased/340.md +++ b/release-notes.d/unreleased/340.md @@ -4,4 +4,4 @@ url: https://github.com/llm-d/llm-d-async/pull/340 author: shimib date: 2026-07-21 --- -Breaking: the Helm chart is renamed from `async-processor` to `llm-d-async` and now publishes to `oci://ghcr.io/llm-d/charts/llm-d-async`. Rendered resources are named `llm-d-async-*` with `app.kubernetes.io/name=llm-d-async`; because a Deployment's selector is immutable, upgrading from an existing `async-processor` install requires uninstall + reinstall. The old `charts/async-processor` package remains available (no redirect) but receives no new releases. +Breaking: the Helm chart is renamed from `async-processor` to `llm-d-async` and now publishes to `oci://ghcr.io/llm-d/charts/llm-d-async`. Rendered resources are named `llm-d-async-*` with `app.kubernetes.io/name=llm-d-async`; because a Deployment's selector is immutable, upgrading from an existing `async-processor` install requires uninstall + reinstall. The old `charts/async-processor` package remains available (no redirect) but receives no new releases. The default OpenTelemetry trace service name also changes from `async-processor` to `llm-d-async` (`OTEL_SERVICE_NAME`) — update any trace queries/dashboards accordingly.