From 32faf9ee77af7730178b9a6df30f6c33954564fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Fernando=20Carri=C3=B3n?= Date: Tue, 21 Jul 2026 13:53:08 +0200 Subject: [PATCH 1/6] DND-1418: support S3Proxy 3.x (appVersion 2.7.0 -> 3.3.0) Bump appVersion to 3.3.0 (latest stable; the chart was several releases behind on 2.7.0). Validated on kind that every functionally-tested backend starts and serves on the 3.3.0 image: s3, filesystem, transient, azureblob (azureblob-sdk), and the multi-backend routing scenario all pass. CI runs the functional legs against the default image tag, which now equals appVersion 3.3.0. Also document S3Proxy version compatibility in the chart README: minimum supported 2.7.0, and the jclouds deprecation path introduced in 3.0.0 (the jclouds providers still work on 3.x but are deprecated; 3.3.0 is the last release to bundle jclouds, after which b2 and rackspace-cloudfiles have no SDK successor). Chart version 0.1.0 -> 0.2.0 (minor: new app major version supported, no breaking chart-values changes). Scope: minimal bump only. Migrating the chart's default providers to their SDK/NIO2 replacements (aws-s3-sdk, google-cloud-storage-sdk, openstack-swift-sdk) and end-of-lifing b2/rackspace is tracked as a separate follow-up. --- charts/s3proxy/Chart.yaml | 4 ++-- charts/s3proxy/README.md.gotmpl | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/charts/s3proxy/Chart.yaml b/charts/s3proxy/Chart.yaml index 4c04de7..a846a36 100644 --- a/charts/s3proxy/Chart.yaml +++ b/charts/s3proxy/Chart.yaml @@ -15,10 +15,10 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 +version: 0.2.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "2.7.0" +appVersion: "3.3.0" diff --git a/charts/s3proxy/README.md.gotmpl b/charts/s3proxy/README.md.gotmpl index 7a5398d..80cceec 100644 --- a/charts/s3proxy/README.md.gotmpl +++ b/charts/s3proxy/README.md.gotmpl @@ -19,6 +19,17 @@ - Helm 3.2.0+ - PV provisioner support in the underlying infrastructure (if using filesystem backend with persistence) +## S3Proxy version compatibility + +The chart tracks S3Proxy (`andrewgaul/s3proxy`) through `appVersion` (currently `{{ template "chart.appVersion" . }}`); override it with `image.tag`. Minimum supported version is `2.7.0`. + +S3Proxy `3.0.0` deprecated the Apache jclouds storage backends (`s3`, `aws-s3`, `azureblob`, `filesystem`, `transient`) in favor of SDK / NIO2 providers, and `3.3.0` is the last release to bundle jclouds. The jclouds providers still function on 3.x but are deprecated upstream: + +- `filesystem` / `transient`: default to the non-deprecated `*-nio2` variants (`nio2: true`). +- `azureblob`: prefer `provider: azureblob-sdk` (the jclouds `azureblob` provider is deprecated and mis-signs against custom endpoints such as Azurite). +- `s3`, `googleCloudStorage`, `openstackSwift`: SDK providers exist upstream (`aws-s3-sdk`, `google-cloud-storage-sdk`, `openstack-swift-sdk`); migrating the chart defaults to them is tracked separately. +- `b2`, `rackspaceCloudfiles`: jclouds-only with no SDK successor; they will stop working on S3Proxy releases after `3.3.0`. + ## Installation ### Install the chart From 87dafb5ab8268b32ff1a0b4091ae13588b301297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Fernando=20Carri=C3=B3n?= Date: Tue, 21 Jul 2026 14:05:36 +0200 Subject: [PATCH 2/6] DND-1418: document jclouds compatibility plan + guard b2/atmos past 3.3.0 README: replace the compatibility note with our actual plan. Frame the jclouds sunset as upstream's announced intent (only Atmos and B2 are explicitly named; no jclouds-free release has shipped yet), stop over-claiming that rackspace breaks (it is Swift-compatible and may be served by openstack-swift-sdk), and state the plan: support through 3.3.0 on 0.x, hard-fail if a no-successor jclouds backend is used past 3.3.0, and move the chart to 1.x.x when S3Proxy ships its first jclouds-free release. configmap.yaml: add a render-time guardrail. If b2 (or atmos, if ever added) is enabled while the effective S3Proxy version (image.tag, else appVersion) parses as semver and is > 3.3.0, fail with an actionable message. Non-semver tags (master, sha-*) are not version-checked; 3.3.0 and earlier are allowed. Verified: default renders clean; b2 + image.tag=3.4.0 fails with the message; b2 on 3.3.0 / non-semver tags / non-jclouds backends all render; helm lint, kubeconform (all test-values), and helm-polish (0 errors) clean. --- charts/s3proxy/README.md.gotmpl | 14 ++++++++++---- charts/s3proxy/templates/configmap.yaml | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/charts/s3proxy/README.md.gotmpl b/charts/s3proxy/README.md.gotmpl index 80cceec..fde4a8e 100644 --- a/charts/s3proxy/README.md.gotmpl +++ b/charts/s3proxy/README.md.gotmpl @@ -23,12 +23,18 @@ The chart tracks S3Proxy (`andrewgaul/s3proxy`) through `appVersion` (currently `{{ template "chart.appVersion" . }}`); override it with `image.tag`. Minimum supported version is `2.7.0`. -S3Proxy `3.0.0` deprecated the Apache jclouds storage backends (`s3`, `aws-s3`, `azureblob`, `filesystem`, `transient`) in favor of SDK / NIO2 providers, and `3.3.0` is the last release to bundle jclouds. The jclouds providers still function on 3.x but are deprecated upstream: +S3Proxy `3.0.0` deprecated the Apache jclouds storage backends (`s3`, `aws-s3`, `azureblob`, `filesystem`, `transient`) in favor of SDK / NIO2 providers. Upstream has announced that `3.3.0` is the last release to bundle jclouds and that future releases "will lack its Atmos and B2 storage backends" (no jclouds-free release has shipped yet; `3.3.0` remains the latest). The jclouds providers still work on 3.x, but are deprecated: -- `filesystem` / `transient`: default to the non-deprecated `*-nio2` variants (`nio2: true`). +- `filesystem` / `transient`: already default to the non-deprecated `*-nio2` variants (`nio2: true`). - `azureblob`: prefer `provider: azureblob-sdk` (the jclouds `azureblob` provider is deprecated and mis-signs against custom endpoints such as Azurite). -- `s3`, `googleCloudStorage`, `openstackSwift`: SDK providers exist upstream (`aws-s3-sdk`, `google-cloud-storage-sdk`, `openstack-swift-sdk`); migrating the chart defaults to them is tracked separately. -- `b2`, `rackspaceCloudfiles`: jclouds-only with no SDK successor; they will stop working on S3Proxy releases after `3.3.0`. +- `s3`, `googleCloudStorage`, `openstackSwift`: SDK providers exist upstream (`aws-s3-sdk`, `google-cloud-storage-sdk`, `openstack-swift-sdk`). `rackspaceCloudfiles` is OpenStack-Swift-compatible and may be served by `openstack-swift-sdk`. Migrating the chart defaults to the SDK providers is tracked separately. +- `b2` (and Atmos, if ever added) are jclouds-only with **no SDK successor**. These are the backends upstream has said future releases will drop. + +### Our compatibility plan + +- **Through S3Proxy `3.3.0` (chart `0.x`):** all current backends, including the jclouds-only `b2`, are supported. This is where the chart sits today. +- **Guardrail:** the chart **hard-fails at render time** if `b2` (or `atmos`) is enabled while the effective S3Proxy version (`image.tag`, else `appVersion`) is greater than `3.3.0`, so an upgrade past the jclouds sunset cannot silently ship a broken backend. Pin `image.tag` to `3.3.0` or earlier, or disable the backend. +- **When S3Proxy ships its first jclouds-free release:** the chart moves to `1.x.x` (major bump), migrates the remaining backends to their SDK providers, and drops `b2`/`atmos`. Tracked in the SDK-migration follow-up. ## Installation diff --git a/charts/s3proxy/templates/configmap.yaml b/charts/s3proxy/templates/configmap.yaml index b1c77a0..6a9ac96 100644 --- a/charts/s3proxy/templates/configmap.yaml +++ b/charts/s3proxy/templates/configmap.yaml @@ -60,6 +60,25 @@ s3proxy.sharded-blobstore.levels=2 s3proxy.large-object-mocking=true {{- end }} {{- end }} +{{- /* + Guardrail (DND-1418): the jclouds-only backends below have no SDK successor, and + upstream has announced they are dropped after S3Proxy 3.3.0. Fail the render if + one is enabled while the effective S3Proxy version (image.tag, else appVersion) + parses as semver and is greater than 3.3.0, so upgrading past the jclouds sunset + cannot silently ship a broken backend. Non-semver tags (master, sha-*) are not + version-checked. When S3Proxy ships a jclouds-free release the chart moves to + 1.x.x and migrates/removes these backends. +*/}} +{{- $s3pVersion := .Values.image.tag | default .Chart.AppVersion -}} +{{- if regexMatch "^v?[0-9]+\\.[0-9]+\\.[0-9]+" $s3pVersion }} + {{- if semverCompare "> 3.3.0" (trimPrefix "v" $s3pVersion) }} + {{- range $backend := list "b2" "atmos" }} + {{- if dig $backend "enabled" false $.Values.config.backends }} + {{- fail (printf "config.backends.%s is enabled, but the %q S3Proxy backend relies on Apache jclouds, which is dropped after 3.3.0 (resolved image version %s). Pin image.tag to 3.3.0 or earlier, or disable config.backends.%s. See the chart README 'S3Proxy version compatibility' notes." $backend $backend $s3pVersion $backend) }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} apiVersion: v1 kind: ConfigMap metadata: From 1634602d55a76477424f7c9461854cbdca78995b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Fernando=20Carri=C3=B3n?= Date: Tue, 21 Jul 2026 14:48:02 +0200 Subject: [PATCH 3/6] DND-1418: default azureblob backend to azureblob-sdk provider On S3Proxy 3.x the legacy jclouds azureblob provider is deprecated and mis-signs against custom endpoints (Azurite, Azure Gov/China, private endpoints); azureblob-sdk (Azure SDK) is the non-deprecated, correctly-signing provider. Change the chart default provider from "azureblob" to "azureblob-sdk". Users can still set provider: azureblob explicitly for the legacy backend until it is removed post-3.3.0. On real Azure, azureblob-sdk may require config.backends.azureblob.regions for bucket creation (see DND-1416). Update the README compatibility note accordingly. Verified: default azureblob render now emits jclouds.provider=azureblob-sdk; helm lint, kubeconform (all test-values), helm-polish (0 errors) clean. --- charts/s3proxy/README.md.gotmpl | 2 +- charts/s3proxy/values.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/charts/s3proxy/README.md.gotmpl b/charts/s3proxy/README.md.gotmpl index fde4a8e..1f34330 100644 --- a/charts/s3proxy/README.md.gotmpl +++ b/charts/s3proxy/README.md.gotmpl @@ -26,7 +26,7 @@ The chart tracks S3Proxy (`andrewgaul/s3proxy`) through `appVersion` (currently S3Proxy `3.0.0` deprecated the Apache jclouds storage backends (`s3`, `aws-s3`, `azureblob`, `filesystem`, `transient`) in favor of SDK / NIO2 providers. Upstream has announced that `3.3.0` is the last release to bundle jclouds and that future releases "will lack its Atmos and B2 storage backends" (no jclouds-free release has shipped yet; `3.3.0` remains the latest). The jclouds providers still work on 3.x, but are deprecated: - `filesystem` / `transient`: already default to the non-deprecated `*-nio2` variants (`nio2: true`). -- `azureblob`: prefer `provider: azureblob-sdk` (the jclouds `azureblob` provider is deprecated and mis-signs against custom endpoints such as Azurite). +- `azureblob`: **defaults to `provider: azureblob-sdk`** (the Azure SDK provider, which signs correctly against custom endpoints such as Azurite). The legacy jclouds `azureblob` provider is deprecated and mis-signs against custom endpoints; set `provider: azureblob` only if you specifically need it. On real Azure, `azureblob-sdk` may require `config.backends.azureblob.regions` for bucket creation. - `s3`, `googleCloudStorage`, `openstackSwift`: SDK providers exist upstream (`aws-s3-sdk`, `google-cloud-storage-sdk`, `openstack-swift-sdk`). `rackspaceCloudfiles` is OpenStack-Swift-compatible and may be served by `openstack-swift-sdk`. Migrating the chart defaults to the SDK providers is tracked separately. - `b2` (and Atmos, if ever added) are jclouds-only with **no SDK successor**. These are the backends upstream has said future releases will drop. diff --git a/charts/s3proxy/values.yaml b/charts/s3proxy/values.yaml index f2eaa36..f53a9a4 100644 --- a/charts/s3proxy/values.yaml +++ b/charts/s3proxy/values.yaml @@ -211,8 +211,8 @@ config: azureblob: # -- Enable Azure Blob Storage backend enabled: false - # -- Provider type (azureblob or azureblob-sdk) - provider: "azureblob" + # -- Provider type. Defaults to `azureblob-sdk` (Azure SDK): it signs correctly against custom endpoints (Azurite, Azure Gov/China, private endpoints) and is the non-deprecated provider on S3Proxy 3.x. The legacy jclouds `azureblob` provider is deprecated upstream and mis-signs against custom endpoints; on real Azure `azureblob-sdk` may require `regions` to be set for bucket creation. + provider: "azureblob-sdk" # -- Storage account name account: "" # -- Storage account key configuration From d7bef241ff4550ddad674a96995399ad4d96055c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Fernando=20Carri=C3=B3n?= Date: Tue, 21 Jul 2026 15:22:02 +0200 Subject: [PATCH 4/6] DND-1418: depend on comet-common library chart and wrap its helpers Add comet-ml/common-helm-chart (comet-common 0.3.0) as a library dependency and route the chart's helpers through it, so naming, labels, and image rendering stay consistent with the other Comet charts. The dependency is vendored in-repo (Chart.lock + charts/comet-common-0.3.0.tgz) so CI and fork PRs resolve it without any registry access, preserving the no-secrets model. - _helpers.tpl: s3proxy.name/fullname/chart/serviceAccountName now wrap comet-common.names.*; s3proxy.labels wraps comet-common.labels.base. - deployment.yaml: both image strings render via comet-common.images.image. - s3proxy.selectorLabels kept local (sourcing the name from comet-common) so the immutable Deployment selector stays {name, instance}; comet-common's selectorLabels would add app.kubernetes.io/component and break upgrades. Render is equivalent: names, selector labels, and image strings are byte-identical to before; the only change is metadata label key ordering (comet-common's order) and the resulting checksum/config|secret annotation recompute. No app.kubernetes.io/component is introduced anywhere. Verified: render diff vs base is label-order + checksum only; helm lint, kubeconform (all test-values), helm-polish (0 errors) clean; kind install resolves the vendored dep, renders image andrewgaul/s3proxy:3.3.0, and multi-backend routing passes. --- charts/s3proxy/Chart.lock | 6 +++ charts/s3proxy/Chart.yaml | 8 +++ charts/s3proxy/charts/comet-common-0.3.0.tgz | Bin 0 -> 19784 bytes charts/s3proxy/templates/_helpers.tpl | 50 ++++++++----------- charts/s3proxy/templates/deployment.yaml | 4 +- 5 files changed, 37 insertions(+), 31 deletions(-) create mode 100644 charts/s3proxy/Chart.lock create mode 100644 charts/s3proxy/charts/comet-common-0.3.0.tgz diff --git a/charts/s3proxy/Chart.lock b/charts/s3proxy/Chart.lock new file mode 100644 index 0000000..9f6f29f --- /dev/null +++ b/charts/s3proxy/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: comet-common + repository: oci://ghcr.io/comet-ml + version: 0.3.0 +digest: sha256:8dfb72314d8825e0fecef7d194fef49be3946cb04537924c56a6921222fc9593 +generated: "2026-07-21T15:17:18.631137876+02:00" diff --git a/charts/s3proxy/Chart.yaml b/charts/s3proxy/Chart.yaml index a846a36..a60f8fc 100644 --- a/charts/s3proxy/Chart.yaml +++ b/charts/s3proxy/Chart.yaml @@ -22,3 +22,11 @@ version: 0.2.0 # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. appVersion: "3.3.0" + +# comet-common is a library chart of shared helpers (names, labels, images, ...) +# used across Comet charts. Vendored in-repo (charts/*.tgz + Chart.lock) so CI and +# fork PRs resolve it without registry access. +dependencies: + - name: comet-common + version: "0.3.0" + repository: "oci://ghcr.io/comet-ml" diff --git a/charts/s3proxy/charts/comet-common-0.3.0.tgz b/charts/s3proxy/charts/comet-common-0.3.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..7ec8089f77811f4cdbb02e5eb2078cc4b647c9ea GIT binary patch literal 19784 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 literal 0 HcmV?d00001 diff --git a/charts/s3proxy/templates/_helpers.tpl b/charts/s3proxy/templates/_helpers.tpl index b3e1b8f..4a865cd 100644 --- a/charts/s3proxy/templates/_helpers.tpl +++ b/charts/s3proxy/templates/_helpers.tpl @@ -1,62 +1,54 @@ +{{- /* +These helpers wrap the comet-common library chart +(https://github.com/comet-ml/common-helm-chart) so naming, labels, and image +rendering stay consistent across Comet charts. Call sites keep using the +s3proxy.* names; only the implementations delegate to comet-common. + +Selector labels are the one exception kept local: comet-common.selectorLabels +adds app.kubernetes.io/component, and a Deployment's spec.selector.matchLabels is +immutable, so adopting it would break `helm upgrade` on existing releases. +*/}} + {{- /* Expand the name of the chart. */}} {{- define "s3proxy.name" -}} - {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- include "comet-common.names.name" . -}} {{- 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 "s3proxy.fullname" -}} - {{- if .Values.fullnameOverride }} - {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} - {{- else }} - {{- $name := default .Chart.Name .Values.nameOverride }} - {{- if contains $name .Release.Name }} - {{- .Release.Name | trunc 63 | trimSuffix "-" }} - {{- else }} - {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} - {{- end }} - {{- end }} +{{- include "comet-common.names.fullname" . -}} {{- end }} {{- /* Create chart name and version as used by the chart label. */}} {{- define "s3proxy.chart" -}} - {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- include "comet-common.names.chart" . -}} {{- end }} {{- /* -Common labels +Common labels (comet-common base set: name, chart, instance, managed-by, version). */}} {{- define "s3proxy.labels" -}} -helm.sh/chart: {{ include "s3proxy.chart" . }} -{{ include "s3proxy.selectorLabels" . }} - {{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} - {{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} +{{ include "comet-common.labels.base" . }} {{- end }} {{- /* -Selector labels +Selector labels. Kept local so the immutable Deployment selector stays +{name, instance}; the name value is sourced from comet-common for consistency. */}} {{- define "s3proxy.selectorLabels" -}} -app.kubernetes.io/name: {{ include "s3proxy.name" . }} +app.kubernetes.io/name: {{ include "comet-common.names.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} {{- /* -Create the name of the service account to use +Create the name of the service account to use. */}} {{- define "s3proxy.serviceAccountName" -}} - {{- if .Values.serviceAccount.create }} - {{- default (include "s3proxy.fullname" .) .Values.serviceAccount.name }} - {{- else }} - {{- default "default" .Values.serviceAccount.name }} - {{- end }} +{{- include "comet-common.names.serviceAccount" . -}} {{- end }} diff --git a/charts/s3proxy/templates/deployment.yaml b/charts/s3proxy/templates/deployment.yaml index 5060456..68f901d 100644 --- a/charts/s3proxy/templates/deployment.yaml +++ b/charts/s3proxy/templates/deployment.yaml @@ -36,7 +36,7 @@ spec: - name: merge-configs securityContext: {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.configMergeImage.repository }}:{{ .Values.configMergeImage.tag }}" + image: {{ include "comet-common.images.image" (dict "imageRoot" .Values.configMergeImage "chart" .Chart) | quote }} imagePullPolicy: {{ .Values.configMergeImage.pullPolicy }} command: ["sh", "-c"] args: @@ -98,7 +98,7 @@ spec: - name: {{ .Chart.Name }} securityContext: {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + image: {{ include "comet-common.images.image" (dict "imageRoot" .Values.image "chart" .Chart) | quote }} imagePullPolicy: {{ .Values.image.pullPolicy }} command: - java From 53b30e3c85ef603d625d9bb2b0f5c06b9147b63a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Fernando=20Carri=C3=B3n?= Date: Tue, 21 Jul 2026 15:31:04 +0200 Subject: [PATCH 5/6] DND-1418: call comet-common helpers directly (drop pass-through wrappers) The s3proxy.name/fullname/chart/serviceAccountName/labels wrappers only delegated to comet-common with no added logic. Remove them and call the comet-common helpers directly at every call site (comet-common.names.*, comet-common.labels.base). _helpers.tpl now keeps only s3proxy.selectorLabels, which does add value: it pins the immutable Deployment selector to {name, instance} (sourcing the name from comet-common) rather than comet-common.selectorLabels, which appends app.kubernetes.io/component. Pure refactor: render is byte-identical to the previous commit across all 11 test-values (0 unexpected diff lines); helm lint, kubeconform, helm-polish all clean. --- charts/s3proxy/templates/NOTES.txt | 10 ++-- charts/s3proxy/templates/_helpers.tpl | 54 ++++--------------- charts/s3proxy/templates/configmap.yaml | 4 +- charts/s3proxy/templates/deployment.yaml | 12 ++--- charts/s3proxy/templates/hpa.yaml | 6 +-- charts/s3proxy/templates/ingress.yaml | 4 +- charts/s3proxy/templates/pvc.yaml | 4 +- charts/s3proxy/templates/secret.yaml | 4 +- charts/s3proxy/templates/service.yaml | 4 +- charts/s3proxy/templates/serviceaccount.yaml | 4 +- .../templates/tests/test-connection.yaml | 6 +-- 11 files changed, 39 insertions(+), 73 deletions(-) diff --git a/charts/s3proxy/templates/NOTES.txt b/charts/s3proxy/templates/NOTES.txt index f25d4bc..ad8cd23 100644 --- a/charts/s3proxy/templates/NOTES.txt +++ b/charts/s3proxy/templates/NOTES.txt @@ -6,16 +6,16 @@ {{- end }} {{- end }} {{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "s3proxy.fullname" . }}) + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "comet-common.names.fullname" . }}) export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") echo http://$NODE_IP:$NODE_PORT {{- else if contains "LoadBalancer" .Values.service.type }} NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "s3proxy.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "s3proxy.fullname" . }} --template "{{ "{{" }} range (index .status.loadBalancer.ingress 0) {{ "}}" }}{{ "{{" }}.{{ "}}" }}{{ "{{" }} end {{ "}}" }}") + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "comet-common.names.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "comet-common.names.fullname" . }} --template "{{ "{{" }} range (index .status.loadBalancer.ingress 0) {{ "}}" }}{{ "{{" }}.{{ "}}" }}{{ "{{" }} end {{ "}}" }}") echo http://$SERVICE_IP:{{ .Values.service.port }} {{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "s3proxy.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "comet-common.names.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") echo "Visit http://127.0.0.1:8080 to use your application" kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT @@ -100,7 +100,7 @@ 4. Monitoring: To check the logs: - kubectl logs -n {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "s3proxy.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" + kubectl logs -n {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "comet-common.names.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" 5. Important Notes: {{- if .Values.config.backends.filesystem.enabled }} diff --git a/charts/s3proxy/templates/_helpers.tpl b/charts/s3proxy/templates/_helpers.tpl index 4a865cd..62b0688 100644 --- a/charts/s3proxy/templates/_helpers.tpl +++ b/charts/s3proxy/templates/_helpers.tpl @@ -1,54 +1,20 @@ {{- /* -These helpers wrap the comet-common library chart -(https://github.com/comet-ml/common-helm-chart) so naming, labels, and image -rendering stay consistent across Comet charts. Call sites keep using the -s3proxy.* names; only the implementations delegate to comet-common. +This chart uses the comet-common library chart +(https://github.com/comet-ml/common-helm-chart) directly for naming, labels, and +image rendering: call sites use comet-common.names.*, comet-common.labels.base, +and comet-common.images.image. Only s3proxy-specific helpers live in this file. -Selector labels are the one exception kept local: comet-common.selectorLabels -adds app.kubernetes.io/component, and a Deployment's spec.selector.matchLabels is -immutable, so adopting it would break `helm upgrade` on existing releases. +s3proxy.selectorLabels is kept local rather than using comet-common.selectorLabels: +the latter adds app.kubernetes.io/component, and a Deployment's +spec.selector.matchLabels is immutable, so adopting it would break `helm upgrade` +on existing releases. */}} {{- /* -Expand the name of the chart. -*/}} -{{- define "s3proxy.name" -}} -{{- include "comet-common.names.name" . -}} -{{- end }} - -{{- /* -Create a default fully qualified app name. -*/}} -{{- define "s3proxy.fullname" -}} -{{- include "comet-common.names.fullname" . -}} -{{- end }} - -{{- /* -Create chart name and version as used by the chart label. -*/}} -{{- define "s3proxy.chart" -}} -{{- include "comet-common.names.chart" . -}} -{{- end }} - -{{- /* -Common labels (comet-common base set: name, chart, instance, managed-by, version). -*/}} -{{- define "s3proxy.labels" -}} -{{ include "comet-common.labels.base" . }} -{{- end }} - -{{- /* -Selector labels. Kept local so the immutable Deployment selector stays -{name, instance}; the name value is sourced from comet-common for consistency. +Selector labels. The immutable Deployment selector stays {name, instance}; the +name value is sourced from comet-common for consistency. */}} {{- define "s3proxy.selectorLabels" -}} app.kubernetes.io/name: {{ include "comet-common.names.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} - -{{- /* -Create the name of the service account to use. -*/}} -{{- define "s3proxy.serviceAccountName" -}} -{{- include "comet-common.names.serviceAccount" . -}} -{{- end }} diff --git a/charts/s3proxy/templates/configmap.yaml b/charts/s3proxy/templates/configmap.yaml index 6a9ac96..1dddf89 100644 --- a/charts/s3proxy/templates/configmap.yaml +++ b/charts/s3proxy/templates/configmap.yaml @@ -82,9 +82,9 @@ s3proxy.large-object-mocking=true apiVersion: v1 kind: ConfigMap metadata: - name: {{ include "s3proxy.fullname" . }} + name: {{ include "comet-common.names.fullname" . }} labels: - {{- include "s3proxy.labels" . | nindent 4 }} + {{- include "comet-common.labels.base" . | nindent 4 }} data: {{- if .Values.config.backends.filesystem.enabled }} backend-filesystem.properties: |- diff --git a/charts/s3proxy/templates/deployment.yaml b/charts/s3proxy/templates/deployment.yaml index 68f901d..4ce926d 100644 --- a/charts/s3proxy/templates/deployment.yaml +++ b/charts/s3proxy/templates/deployment.yaml @@ -1,9 +1,9 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: {{ include "s3proxy.fullname" . }} + name: {{ include "comet-common.names.fullname" . }} labels: - {{- include "s3proxy.labels" . | nindent 4 }} + {{- include "comet-common.labels.base" . | nindent 4 }} spec: {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount }} @@ -29,7 +29,7 @@ spec: imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} - serviceAccountName: {{ include "s3proxy.serviceAccountName" . }} + serviceAccountName: {{ include "comet-common.names.serviceAccount" . }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} initContainers: @@ -186,17 +186,17 @@ spec: volumes: - name: config configMap: - name: {{ include "s3proxy.fullname" . }} + name: {{ include "comet-common.names.fullname" . }} - name: secret-config secret: - secretName: {{ include "s3proxy.fullname" . }} + secretName: {{ include "comet-common.names.fullname" . }} - name: merged-config emptyDir: {} {{- if .Values.config.backends.filesystem.enabled }} {{- if .Values.persistence.enabled }} - name: data persistentVolumeClaim: - claimName: {{ .Values.persistence.existingClaim | default (include "s3proxy.fullname" .) }} + claimName: {{ .Values.persistence.existingClaim | default (include "comet-common.names.fullname" .) }} {{- end }} {{- end }} {{- if and .Values.config.backends.googleCloudStorage.enabled .Values.config.backends.googleCloudStorage.privateKey.existingSecret }} diff --git a/charts/s3proxy/templates/hpa.yaml b/charts/s3proxy/templates/hpa.yaml index ad9b662..6d5019b 100644 --- a/charts/s3proxy/templates/hpa.yaml +++ b/charts/s3proxy/templates/hpa.yaml @@ -2,14 +2,14 @@ apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: - name: {{ include "s3proxy.fullname" . }} + name: {{ include "comet-common.names.fullname" . }} labels: - {{- include "s3proxy.labels" . | nindent 4 }} + {{- include "comet-common.labels.base" . | nindent 4 }} spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment - name: {{ include "s3proxy.fullname" . }} + name: {{ include "comet-common.names.fullname" . }} minReplicas: {{ .Values.autoscaling.minReplicas }} maxReplicas: {{ .Values.autoscaling.maxReplicas }} metrics: diff --git a/charts/s3proxy/templates/ingress.yaml b/charts/s3proxy/templates/ingress.yaml index c7f1df5..9e9d64e 100644 --- a/charts/s3proxy/templates/ingress.yaml +++ b/charts/s3proxy/templates/ingress.yaml @@ -1,5 +1,5 @@ {{- if .Values.ingress.enabled -}} - {{- $fullName := include "s3proxy.fullname" . -}} + {{- $fullName := include "comet-common.names.fullname" . -}} {{- $svcPort := .Values.service.port -}} {{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} @@ -17,7 +17,7 @@ kind: Ingress metadata: name: {{ $fullName }} labels: - {{- include "s3proxy.labels" . | nindent 4 }} + {{- include "comet-common.labels.base" . | nindent 4 }} {{- with .Values.ingress.annotations }} annotations: {{- toYaml . | nindent 4 }} diff --git a/charts/s3proxy/templates/pvc.yaml b/charts/s3proxy/templates/pvc.yaml index b56d585..a188423 100644 --- a/charts/s3proxy/templates/pvc.yaml +++ b/charts/s3proxy/templates/pvc.yaml @@ -2,9 +2,9 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: - name: {{ include "s3proxy.fullname" . }} + name: {{ include "comet-common.names.fullname" . }} labels: - {{- include "s3proxy.labels" . | nindent 4 }} + {{- include "comet-common.labels.base" . | nindent 4 }} {{- with .Values.persistence.annotations }} annotations: {{- toYaml . | nindent 4 }} diff --git a/charts/s3proxy/templates/secret.yaml b/charts/s3proxy/templates/secret.yaml index 26a01f0..38b1b0f 100644 --- a/charts/s3proxy/templates/secret.yaml +++ b/charts/s3proxy/templates/secret.yaml @@ -1,9 +1,9 @@ apiVersion: v1 kind: Secret metadata: - name: {{ include "s3proxy.fullname" . }} + name: {{ include "comet-common.names.fullname" . }} labels: - {{- include "s3proxy.labels" . | nindent 4 }} + {{- include "comet-common.labels.base" . | nindent 4 }} type: Opaque stringData: # Sensitive properties merged into the backend properties files by the diff --git a/charts/s3proxy/templates/service.yaml b/charts/s3proxy/templates/service.yaml index b952f86..e4868c2 100644 --- a/charts/s3proxy/templates/service.yaml +++ b/charts/s3proxy/templates/service.yaml @@ -1,9 +1,9 @@ apiVersion: v1 kind: Service metadata: - name: {{ include "s3proxy.fullname" . }} + name: {{ include "comet-common.names.fullname" . }} labels: - {{- include "s3proxy.labels" . | nindent 4 }} + {{- include "comet-common.labels.base" . | nindent 4 }} {{- with .Values.service.annotations }} annotations: {{- toYaml . | nindent 4 }} diff --git a/charts/s3proxy/templates/serviceaccount.yaml b/charts/s3proxy/templates/serviceaccount.yaml index 32bdbd7..d9add96 100644 --- a/charts/s3proxy/templates/serviceaccount.yaml +++ b/charts/s3proxy/templates/serviceaccount.yaml @@ -2,9 +2,9 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: {{ include "s3proxy.serviceAccountName" . }} + name: {{ include "comet-common.names.serviceAccount" . }} labels: - {{- include "s3proxy.labels" . | nindent 4 }} + {{- include "comet-common.labels.base" . | nindent 4 }} {{- with .Values.serviceAccount.annotations }} annotations: {{- toYaml . | nindent 4 }} diff --git a/charts/s3proxy/templates/tests/test-connection.yaml b/charts/s3proxy/templates/tests/test-connection.yaml index 24edcdb..86cdc24 100644 --- a/charts/s3proxy/templates/tests/test-connection.yaml +++ b/charts/s3proxy/templates/tests/test-connection.yaml @@ -1,9 +1,9 @@ apiVersion: v1 kind: Pod metadata: - name: "{{ include "s3proxy.fullname" . }}-test-connection" + name: "{{ include "comet-common.names.fullname" . }}-test-connection" labels: - {{- include "s3proxy.labels" . | nindent 4 }} + {{- include "comet-common.labels.base" . | nindent 4 }} annotations: "helm.sh/hook": test spec: @@ -11,5 +11,5 @@ spec: - name: wget image: busybox command: ['wget'] - args: ['{{ include "s3proxy.fullname" . }}:{{ .Values.service.port }}'] + args: ['{{ include "comet-common.names.fullname" . }}:{{ .Values.service.port }}'] restartPolicy: Never From 483b3134875849efcea14a0cb2432ef7809305ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Fernando=20Carri=C3=B3n?= Date: Tue, 21 Jul 2026 15:37:38 +0200 Subject: [PATCH 6/6] DND-1418: don't vendor the comet-common subchart; resolve it in CI Git-ignore charts/s3proxy/charts/ and stop checking in the resolved comet-common-0.3.0.tgz. Chart.lock (committed) pins the version/digest, and each workflow that renders, installs, or packages the chart now runs `helm dependency build charts/s3proxy` first. comet-common's OCI package is anonymously pullable, so this needs no registry auth and still works on fork PRs. - .gitignore: ignore charts/s3proxy/charts/. - lint-render / functional-test / release: add a "Build chart dependencies" step after Set up Helm (release also gains a Set up Helm step). - helm-diff: build deps for whichever side (base/head) has a Chart.lock before rendering (base may predate the dependency). Verified: removing the local .tgz and running `helm dependency build` from Chart.lock repopulates it and the chart renders; actionlint clean on the changed render/install workflows; helm-polish clean. --- .github/workflows/functional-test.yaml | 5 +++++ .github/workflows/helm-diff.yaml | 12 ++++++++++++ .github/workflows/lint-render.yaml | 5 +++++ .github/workflows/release.yaml | 10 ++++++++++ .gitignore | 3 +++ charts/s3proxy/Chart.yaml | 6 ++++-- charts/s3proxy/charts/comet-common-0.3.0.tgz | Bin 19784 -> 0 bytes 7 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 .gitignore delete mode 100644 charts/s3proxy/charts/comet-common-0.3.0.tgz diff --git a/.github/workflows/functional-test.yaml b/.github/workflows/functional-test.yaml index 793e745..d6b8020 100644 --- a/.github/workflows/functional-test.yaml +++ b/.github/workflows/functional-test.yaml @@ -71,6 +71,11 @@ jobs: with: version: v3.19.2 + - name: Build chart dependencies + # comet-common is not vendored in-repo; resolve it from Chart.lock + # (anonymous OCI pull, no auth needed). + run: helm dependency build charts/s3proxy + - name: Create kind cluster uses: helm/kind-action@ef37e7f390d99f746eb8b610417061a60e82a6cc # v1.14.0 diff --git a/.github/workflows/helm-diff.yaml b/.github/workflows/helm-diff.yaml index ddd65ec..39f2c25 100644 --- a/.github/workflows/helm-diff.yaml +++ b/.github/workflows/helm-diff.yaml @@ -48,6 +48,18 @@ jobs: with: version: v3.19.2 + - name: Build chart dependencies + # comet-common is not vendored in-repo; resolve it from Chart.lock on each + # side that has one (base may predate the dependency), before rendering. + run: | + for dir in base-repo pr-repo + do + if [ -f "${dir}/${CHART_PATH}/Chart.lock" ] + then + helm dependency build "${dir}/${CHART_PATH}" + fi + done + - name: Render and diff run: | set -uo pipefail diff --git a/.github/workflows/lint-render.yaml b/.github/workflows/lint-render.yaml index 8e1f9e2..07d2c75 100644 --- a/.github/workflows/lint-render.yaml +++ b/.github/workflows/lint-render.yaml @@ -45,6 +45,11 @@ jobs: with: version: v3.19.2 + - name: Build chart dependencies + # comet-common is not vendored in-repo; resolve it from Chart.lock + # (anonymous OCI pull, no auth needed). + run: helm dependency build charts/s3proxy + - name: Helm lint run: helm lint charts/s3proxy --values "${{ matrix.values }}" diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 4c9cc4d..52d2e7c 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -81,6 +81,16 @@ jobs: run: | cp README.md charts/s3proxy/ + - name: Set up Helm + uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1 + with: + version: v3.19.2 + + - name: Build chart dependencies + # comet-common is not vendored in-repo; resolve it from Chart.lock + # (anonymous OCI pull) so chart-releaser can package the chart. + run: helm dependency build charts/s3proxy + - name: Run chart-releaser uses: bitdeps/helm-oci-charts-releaser@v0.1.3 with: diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b64e6d8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Vendored Helm chart dependencies. Not checked in; resolved from Chart.lock via +# `helm dependency build` (CI does this before lint/template/install/package). +charts/s3proxy/charts/ diff --git a/charts/s3proxy/Chart.yaml b/charts/s3proxy/Chart.yaml index a60f8fc..51e12b7 100644 --- a/charts/s3proxy/Chart.yaml +++ b/charts/s3proxy/Chart.yaml @@ -24,8 +24,10 @@ version: 0.2.0 appVersion: "3.3.0" # comet-common is a library chart of shared helpers (names, labels, images, ...) -# used across Comet charts. Vendored in-repo (charts/*.tgz + Chart.lock) so CI and -# fork PRs resolve it without registry access. +# used across Comet charts. The resolved dependency (charts/*.tgz) is git-ignored, +# not checked in; Chart.lock pins the version/digest and CI runs +# `helm dependency build` (anonymous OCI pull, no auth) before lint/template/ +# install/package. dependencies: - name: comet-common version: "0.3.0" diff --git a/charts/s3proxy/charts/comet-common-0.3.0.tgz b/charts/s3proxy/charts/comet-common-0.3.0.tgz deleted file mode 100644 index 7ec8089f77811f4cdbb02e5eb2078cc4b647c9ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19784 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