From 4a335118493fa9e129df5c40529c4b856575f711 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Wed, 4 Mar 2026 19:28:11 -0600 Subject: [PATCH 01/37] Combine configmap-default.yaml and configmap-instance.yaml into configmap-pelican.yaml Since both of these files contain Pelican configuration, splitting them up hurts the organization because the information is not in one place. Co-authored-by: Copilot --- AGENTS.md | 5 ++--- README.md | 9 ++++----- templates/configmap-instance.yaml | 9 --------- ...ap-default.yaml => configmap-pelican.yaml} | 5 ++++- templates/deployment.yaml | 20 +++++++++---------- 5 files changed, 19 insertions(+), 29 deletions(-) delete mode 100644 templates/configmap-instance.yaml rename templates/{configmap-default.yaml => configmap-pelican.yaml} (79%) diff --git a/AGENTS.md b/AGENTS.md index ea41733..298748b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -45,8 +45,7 @@ pelican-cache/ ├── service.yaml # LoadBalancer Service ├── networkpolicy.yaml # NetworkPolicy (conditional) ├── certificate.yaml # cert-manager Certificate (conditional) - ├── configmap-default.yaml # Base pelican.yaml (paths, ports, ConfigLocations) - ├── configmap-instance.yaml # Instance config from values (50-instance.yaml) + ├── configmap-pelican.yaml # Base + instance Pelican config (pelican.yaml + 50-instance.yaml) ├── configmap-logrotate.yaml # Logrotate configuration ├── configmap-xrootd.yaml # Custom xrootd.conf (conditional) ├── pvc-cache-data.yaml # Cache data PVC (conditional on storageType=pvc) @@ -58,7 +57,7 @@ pelican-cache/ ## Key Design Decisions -1. **Pelican config layering**: Two ConfigMaps → `default-config` (infrastructure paths) and `instance-config` (user settings). Pelican's `ConfigLocations` directive merges them in order. The `config-dir-placeholder` emptyDir exists solely because Pelican requires all `ConfigLocations` directories to exist at startup, even if empty (the `/usr/share/pelican/config.d` location is used by the OSDF overlay layer in the Kustomize deployment but is kept as a placeholder here for compatibility). +1. **Pelican config layering**: One ConfigMap (`pelican-config`) contains both `pelican.yaml` (infrastructure paths) and `50-instance.yaml` (user settings). Pelican's `ConfigLocations` directive merges the files in order. The `config-dir-placeholder` emptyDir exists solely because Pelican requires all `ConfigLocations` directories to exist at startup, even if empty (the `/usr/share/pelican/config.d` location is used by the OSDF overlay layer in the Kustomize deployment but is kept as a placeholder here for compatibility). 2. **Secrets are never chart-managed**: The chart only _references_ pre-existing Secrets. This is intentional — issuer keys, OIDC credentials, TLS certs, and passwords are sensitive and should be managed via SealedSecrets, External Secrets Operator, or manual creation. diff --git a/README.md b/README.md index a9ff25a..143b600 100644 --- a/README.md +++ b/README.md @@ -51,10 +51,9 @@ The chart deploys a single Pod with up to three containers: ### Pelican Configuration Layering -Pelican supports loading configuration from multiple files via `ConfigLocations`. This chart generates two ConfigMaps that mirror how the Kustomize deployment layers configuration: +Pelican supports loading configuration from multiple files via `ConfigLocations`. This chart now generates a single ConfigMap that contains both layers: -1. **`default-config`** (mounted at `/etc/pelican/pelican.yaml`) — Fixed infrastructure config: `ConfigLocations`, `IssuerKey` path, `Cache.Port`, `Server.WebPort`, TLS cert paths, log file location. -2. **`instance-config`** (mounted at `/etc/pelican/config.d/50-instance.yaml`) — Generated from your values: federation URL, hostname, cache tuning, OIDC, Lotman, logging levels, XRootD settings, and any `extraPelicanConfig`. +1. **`pelican-config`** (mounted at `/etc/pelican/pelican.yaml` and `/etc/pelican/config.d/50-instance.yaml`) — Includes fixed infrastructure config (`pelican.yaml`) and generated instance settings (`50-instance.yaml`) from your values: federation URL, hostname, cache tuning, OIDC, Lotman, logging levels, XRootD settings, and any `extraPelicanConfig`. Pelican merges these in order, with later files taking precedence. @@ -369,10 +368,10 @@ This chart is a standalone replacement for the Kustomize + Flux deployment model | Kustomize layer | Helm equivalent | |---|---| -| `base/pelican-cache/pelican.yaml` | `configmap-default.yaml` template (fixed) | +| `base/pelican-cache/pelican.yaml` | `configmap-pelican.yaml` template (fixed) | | `base/pelican-cache/deployment.yaml` | `deployment.yaml` template | | `base/osdf-pelican-cache/10-osdf.yaml` | `federation.discoveryUrl` value | -| Instance `50-instance.yaml` | Generated by `_helpers.tpl` → `configmap-instance.yaml` | +| Instance `50-instance.yaml` | Generated by `_helpers.tpl` and stored in `configmap-pelican.yaml` | | Instance `deployment-patch.yaml` | Values: `resources`, `nodeSelector`, `securityContext`, `extraEnv`, etc. | | Instance `service-patch.yaml` | Values: `service.*` | | Instance `cert-patch.yaml` | Values: `certificate.dnsNames` | diff --git a/templates/configmap-instance.yaml b/templates/configmap-instance.yaml deleted file mode 100644 index f69d2c5..0000000 --- a/templates/configmap-instance.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "pelican-cache.fullname" . }}-instance-config - labels: - {{- include "pelican-cache.labels" . | nindent 4 }} -data: - 50-instance.yaml: | -{{- include "pelican-cache.instanceConfig" . | nindent 4 }} diff --git a/templates/configmap-default.yaml b/templates/configmap-pelican.yaml similarity index 79% rename from templates/configmap-default.yaml rename to templates/configmap-pelican.yaml index 013726d..2366e80 100644 --- a/templates/configmap-default.yaml +++ b/templates/configmap-pelican.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: ConfigMap metadata: - name: {{ include "pelican-cache.fullname" . }}-default-config + name: {{ include "pelican-cache.fullname" . }}-pelican-config labels: {{- include "pelican-cache.labels" . | nindent 4 }} data: @@ -25,3 +25,6 @@ data: Logging: LogLocation: "/var/log/pelican/pelican.log" + + 50-instance.yaml: | +{{- include "pelican-cache.instanceConfig" . | nindent 4 }} diff --git a/templates/deployment.yaml b/templates/deployment.yaml index 7bf8b51..765e9e8 100644 --- a/templates/deployment.yaml +++ b/templates/deployment.yaml @@ -14,8 +14,7 @@ spec: template: metadata: annotations: - checksum/default-config: {{ include (print $.Template.BasePath "/configmap-default.yaml") . | sha256sum }} - checksum/instance-config: {{ include (print $.Template.BasePath "/configmap-instance.yaml") . | sha256sum }} + checksum/pelican-config: {{ include (print $.Template.BasePath "/configmap-pelican.yaml") . | sha256sum }} {{- with .Values.podAnnotations }} {{- toYaml . | nindent 8 }} {{- end }} @@ -71,8 +70,9 @@ spec: - name: default-config mountPath: /etc/pelican/pelican.yaml subPath: pelican.yaml - - name: instance-config - mountPath: /etc/pelican/config.d + - name: default-config + mountPath: /etc/pelican/config.d/50-instance.yaml + subPath: 50-instance.yaml - name: cache-data mountPath: /cache - name: logging-volume @@ -161,19 +161,17 @@ spec: path: tls.key mode: 0600 - # Base Pelican configuration (paths, ports, ConfigLocations) + # Pelican configuration (base + instance settings) - name: default-config configMap: - name: {{ include "pelican-cache.fullname" . }}-default-config + name: {{ include "pelican-cache.fullname" . }}-pelican-config items: - key: pelican.yaml path: pelican.yaml mode: 0644 - - # Instance-specific Pelican configuration - - name: instance-config - configMap: - name: {{ include "pelican-cache.fullname" . }}-instance-config + - key: 50-instance.yaml + path: 50-instance.yaml + mode: 0644 # Logrotate configuration - name: log-rotate-config From 083b0c3fc902e84be7504e95706519d24fd72da0 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Wed, 4 Mar 2026 19:24:18 -0600 Subject: [PATCH 02/37] Various tweaks * Use chart's appVersion as the default Pelican image tag. * Remove `app: pelican-cache` label - Helm's own labels are sufficient. * Remove deprecated `Cache.DataLocation` var. * Remove the config-dir-placeholder; in current images, it is unnecessary. * Avoid having an empty Cache block if none of the cache options are customized. * Rename the "cache-data" volume to just "data" to avoid having a "pelican-cache-cache-data" name when the prefixes are applied. * Add a note explaining why we can't mount the TLS cert/key at Pelican's default location. * Change UIAdminUsers to a YAML list instead of space-separated values. * Remove 'federation' from selector labels because selector labels are immutable and prevent chart upgrades in case the federation changes. Co-authored-by: Copilot --- AGENTS.md | 4 ++-- Chart.yaml | 2 +- ci/uw-osdf-cache-values.yaml | 8 ++++++- templates/_helpers.tpl | 37 ++++++++++++++------------------ templates/configmap-pelican.yaml | 2 +- templates/deployment.yaml | 21 ++++++++---------- templates/pvc-cache-data.yaml | 2 +- values.yaml | 7 +++--- 8 files changed, 41 insertions(+), 42 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 298748b..ffb0fe4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -57,7 +57,7 @@ pelican-cache/ ## Key Design Decisions -1. **Pelican config layering**: One ConfigMap (`pelican-config`) contains both `pelican.yaml` (infrastructure paths) and `50-instance.yaml` (user settings). Pelican's `ConfigLocations` directive merges the files in order. The `config-dir-placeholder` emptyDir exists solely because Pelican requires all `ConfigLocations` directories to exist at startup, even if empty (the `/usr/share/pelican/config.d` location is used by the OSDF overlay layer in the Kustomize deployment but is kept as a placeholder here for compatibility). +1. **Pelican config layering**: One ConfigMap (`pelican-config`) contains both `pelican.yaml` (infrastructure paths) and `50-instance.yaml` (user settings). Pelican's `ConfigLocations` directive merges the files in order. 2. **Secrets are never chart-managed**: The chart only _references_ pre-existing Secrets. This is intentional — issuer keys, OIDC credentials, TLS certs, and passwords are sensitive and should be managed via SealedSecrets, External Secrets Operator, or manual creation. @@ -77,7 +77,7 @@ pelican-cache/ - **IssuerKey** is a JWK used to sign tokens. When using a PVC, Pelican auto-generates it on first start. When using an existing Secret, the operator provides a pre-generated key. - **Lotman** = lot-based storage management (experimental). Manages disk quotas per "lot." - **CVMFS port redirector** = a sidecar that redirects legacy CVMFS clients (port 8000) to the Pelican cache. -- **Cache.StorageLocation** vs **Cache.DataLocation**: In Pelican 7.12+, `StorageLocation` is preferred. `DataLocation` is kept as `UNUSED` for backward compatibility with the config schema. +- **Cache.StorageLocation**: In Pelican 7.12+, `StorageLocation` is the preferred cache path setting. - **`Cache.HighWaterMark` / `LowWaterMark`**: XRootD's cache eviction thresholds. When total disk usage crosses the high watermark, files are evicted until it drops below the low watermark. - **`Files*Size` parameters**: Fine-grained diskusage tracking specific to the mount where cache data lives. `FilesMaxSize` must be lower than the low water mark. - **`Cache.Concurrency`**: Guidance is 10× the number of CPU cores. diff --git a/Chart.yaml b/Chart.yaml index 3c4b244..5b578f7 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -3,7 +3,7 @@ name: pelican-cache description: A Helm chart for deploying a Pelican Platform cache in the OSDF type: application version: 0.1.0 -appVersion: "7.23.0" +appVersion: "v7.23.0" home: https://pelicanplatform.org sources: - https://github.com/PelicanPlatform/pelican diff --git a/ci/uw-osdf-cache-values.yaml b/ci/uw-osdf-cache-values.yaml index c719e6c..d321054 100644 --- a/ci/uw-osdf-cache-values.yaml +++ b/ci/uw-osdf-cache-values.yaml @@ -77,7 +77,13 @@ oidc: enabled: true existingSecret: osdf-component-oidc -adminUsers: "http://cilogon.org/serverE/users/133679 http://cilogon.org/serverA/users/10832 http://cilogon.org/serverA/users/21441 http://cilogon.org/serverA/users/46022246 http://cilogon.org/serverA/users/9265706 http://cilogon.org/serverE/users/57152" +adminUsers: + - "http://cilogon.org/serverE/users/133679" + - "http://cilogon.org/serverA/users/10832" + - "http://cilogon.org/serverA/users/21441" + - "http://cilogon.org/serverA/users/46022246" + - "http://cilogon.org/serverA/users/9265706" + - "http://cilogon.org/serverE/users/57152" webPasswordSecret: osdf-prod-pelican-webserver-passwd webPasswordSecretKey: osdf-pelican-webserver-passwd diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index 181427c..9af7882 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -35,6 +35,9 @@ Common labels {{- define "pelican-cache.labels" -}} helm.sh/chart: {{ include "pelican-cache.chart" . }} {{ include "pelican-cache.selectorLabels" . }} +{{- if .Values.federation.label }} +federation: {{ .Values.federation.label }} +{{- end }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} @@ -47,10 +50,6 @@ Selector labels {{- define "pelican-cache.selectorLabels" -}} app.kubernetes.io/name: {{ include "pelican-cache.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} -app: pelican-cache -{{- if .Values.federation.label }} -federation: {{ .Values.federation.label }} -{{- end }} {{- end }} {{/* @@ -87,39 +86,39 @@ Federation: Server: Hostname: {{ required "serverHostname is required" .Values.serverHostname | quote }} {{- if .Values.adminUsers }} - UIAdminUsers: {{ .Values.adminUsers | quote }} + UIAdminUsers: + {{- toYaml .Values.adminUsers | nindent 4 }} {{- end }} {{- if .Values.webPasswordSecret }} UIPasswordFile: /etc/pelican/web-passwd/password {{- end }} -Cache: {{- if .Values.cache.blocksToPrefetch }} - BlocksToPrefetch: {{ .Values.cache.blocksToPrefetch }} +Cache.BlocksToPrefetch: {{ .Values.cache.blocksToPrefetch }} {{- end }} {{- if .Values.oidc.enabled }} - EnableOIDC: true +Cache.EnableOIDC: true {{- end }} {{- if .Values.lotman.enabled }} - EnableLotman: true +Cache.EnableLotman: true {{- end }} {{- if .Values.cache.highWaterMark }} - HighWaterMark: {{ .Values.cache.highWaterMark }} +Cache.HighWaterMark: {{ .Values.cache.highWaterMark }} {{- end }} {{- if .Values.cache.lowWaterMark }} - LowWaterMark: {{ .Values.cache.lowWaterMark }} +Cache.LowWaterMark: {{ .Values.cache.lowWaterMark }} {{- end }} {{- if .Values.cache.filesMaxSize }} - FilesMaxSize: {{ .Values.cache.filesMaxSize }} +Cache.FilesMaxSize: {{ .Values.cache.filesMaxSize }} {{- end }} {{- if .Values.cache.filesNominalSize }} - FilesNominalSize: {{ .Values.cache.filesNominalSize }} +Cache.FilesNominalSize: {{ .Values.cache.filesNominalSize }} {{- end }} {{- if .Values.cache.filesBaseSize }} - FilesBaseSize: {{ .Values.cache.filesBaseSize }} +Cache.FilesBaseSize: {{ .Values.cache.filesBaseSize }} {{- end }} {{- if .Values.cache.concurrency }} - Concurrency: {{ .Values.cache.concurrency }} +Cache.Concurrency: {{ .Values.cache.concurrency }} {{- end }} {{- if .Values.oidc.enabled }} @@ -142,15 +141,11 @@ Lotman: LotHome: /var/lib/pelican/lotman {{- end }} -{{- if or .Values.xrootd.sitename .Values.xrootd.extraConfig }} - -XrootD: {{- if .Values.xrootd.sitename }} - Sitename: {{ .Values.xrootd.sitename | quote }} +XrootD.Sitename: {{ .Values.xrootd.sitename | quote }} {{- end }} {{- if .Values.xrootd.extraConfig }} - ConfigFile: /etc/pelican/xrootd.conf -{{- end }} +XrootD.ConfigFile: /etc/pelican/xrootd.conf {{- end }} {{- if .Values.extraPelicanConfig }} diff --git a/templates/configmap-pelican.yaml b/templates/configmap-pelican.yaml index 2366e80..5bbb81c 100644 --- a/templates/configmap-pelican.yaml +++ b/templates/configmap-pelican.yaml @@ -5,6 +5,7 @@ metadata: labels: {{- include "pelican-cache.labels" . | nindent 4 }} data: +{{/*See note in deployment.yaml for why we have to change TlsCertificate and TlsKey*/}} pelican.yaml: | --- ConfigLocations: @@ -15,7 +16,6 @@ data: Cache: StorageLocation: /cache - DataLocation: UNUSED Port: 8443 Server: diff --git a/templates/deployment.yaml b/templates/deployment.yaml index 765e9e8..438c91c 100644 --- a/templates/deployment.yaml +++ b/templates/deployment.yaml @@ -41,7 +41,7 @@ spec: containers: # ---- Main Pelican cache container ---- - name: pelican-cache - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" args: [] imagePullPolicy: {{ .Values.image.pullPolicy }} {{- with .Values.resources.cache }} @@ -64,16 +64,13 @@ spec: containerPort: 443 protocol: TCP volumeMounts: - # Pelican requires ConfigLocations directories to exist - - name: config-dir-placeholder - mountPath: /usr/share/pelican/config.d - name: default-config mountPath: /etc/pelican/pelican.yaml subPath: pelican.yaml - name: default-config mountPath: /etc/pelican/config.d/50-instance.yaml subPath: 50-instance.yaml - - name: cache-data + - name: data mountPath: /cache - name: logging-volume mountPath: /var/log/pelican @@ -87,6 +84,10 @@ spec: mountPath: /etc/pelican/jwks {{- end }} - name: tls-cert +{{/* We cannot mount the host cert secret at /etc/pelican/certificates +because that would make that directory read-only and Pelican needs to write +its own CA there. So we use a different directory here and in the Pelican config. +*/}} mountPath: /etc/pelican/cert-orig {{- if .Values.lotman.enabled }} - name: lotman-data @@ -145,10 +146,6 @@ spec: mountPath: /etc/logrotate.d/ volumes: - # Placeholder so /usr/share/pelican/config.d exists (required by ConfigLocations) - - name: config-dir-placeholder - emptyDir: {} - # TLS certificate (from cert-manager or existing Secret) - name: tls-cert secret: @@ -180,11 +177,11 @@ spec: # Cache data volume (PVC or hostPath) {{- if eq .Values.cache.storageType "pvc" }} - - name: cache-data + - name: data persistentVolumeClaim: - claimName: {{ include "pelican-cache.fullname" . }}-cache-data + claimName: {{ include "pelican-cache.fullname" . }}-data {{- else }} - - name: cache-data + - name: data hostPath: path: {{ required "cache.hostPath is required when cache.storageType is hostPath" .Values.cache.hostPath }} type: Directory diff --git a/templates/pvc-cache-data.yaml b/templates/pvc-cache-data.yaml index 8ba7e92..f116c2f 100644 --- a/templates/pvc-cache-data.yaml +++ b/templates/pvc-cache-data.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: - name: {{ include "pelican-cache.fullname" . }}-cache-data + name: {{ include "pelican-cache.fullname" . }}-data labels: {{- include "pelican-cache.labels" . | nindent 4 }} spec: diff --git a/values.yaml b/values.yaml index 9647e20..ec9f73f 100644 --- a/values.yaml +++ b/values.yaml @@ -19,7 +19,7 @@ federation: # -- Cache container image image: repository: hub.opensciencegrid.org/pelican_platform/osdf-cache - tag: "v7.23.0" + tag: "" pullPolicy: Always # -- Logrotate sidecar image @@ -148,8 +148,9 @@ oidc: # -- Name of an existing Secret with keys "client.id" and "client.secret" existingSecret: "" -# -- Space-separated list of CILogon admin user identities for the web UI -adminUsers: "" +# -- List of CILogon admin user identities for the web UI +adminUsers: [] +# - "http://cilogon.org/serverA/users/12345" # -- Name of an existing Secret containing the web UI password file webPasswordSecret: "" From a4d533e7d9a877dadb114573dccd1b7e6e88a5dd Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Thu, 19 Mar 2026 17:31:20 -0500 Subject: [PATCH 03/37] Add the serverHostname to the list of DNS names requested in the certificate. certificate.dnsNames will be a list of additional SANs instead. This avoids an admission webhook error with the default values when only serverHostname is specified. Co-authored-by: Copilot --- README.md | 10 ++++++---- templates/NOTES.txt | 10 +--------- templates/certificate.yaml | 5 ++--- values.yaml | 2 +- 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 143b600..09cd1f0 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,7 @@ This Helm chart collapses these three layers into a single, parameterized chart helm install my-cache ./pelican-cache \ --set serverHostname=my-cache.example.com \ --set cache.storageClassName=my-storage-class \ - --set logging.storageClassName=my-storage-class \ - --set certificate.dnsNames[0]=my-cache.example.com + --set logging.storageClassName=my-storage-class ``` ### Installation with a values file @@ -129,12 +128,14 @@ For details on `Files*Size` parameters, see the [XRootD PFC documentation](https ### TLS / Certificates +`serverHostname` is always included in the rendered cert-manager `Certificate.spec.dnsNames`; use `certificate.dnsNames` only for additional SANs. + | Parameter | Default | Description | |---|---|---| | `certificate.enabled` | `true` | Create a cert-manager Certificate | | `certificate.issuerRef.name` | `letsencrypt-prod` | Issuer name | | `certificate.issuerRef.kind` | `ClusterIssuer` | Issuer kind | -| `certificate.dnsNames` | `[]` | DNS SANs (should include `serverHostname`) | +| `certificate.dnsNames` | `[]` | Additional DNS SANs (`serverHostname` is always included) | | `tls.existingSecret` | `""` | Use an existing TLS Secret instead of cert-manager | ### Service @@ -234,7 +235,7 @@ logging: certificate: dnsNames: - - my-cache.osg-htc.org + - my-cache-alt.example.org ``` ### Production Cache (hostPath, like uw-osdf-cache) @@ -258,6 +259,7 @@ namespaceKey: existingSecret: my-cache-issuer-key certificate: + enabled: true dnsNames: - osdf-uw-cache.svc.osdf-prod.chtc.io - osdf-uw-cache.svc.osg-htc.org diff --git a/templates/NOTES.txt b/templates/NOTES.txt index 1d32822..0fc6ea2 100644 --- a/templates/NOTES.txt +++ b/templates/NOTES.txt @@ -8,15 +8,7 @@ Pelican Cache {{ .Chart.AppVersion }} has been deployed. {{- if .Values.certificate.enabled }} TLS certificate will be issued by {{ .Values.certificate.issuerRef.kind }}/{{ .Values.certificate.issuerRef.name }}. -{{- if .Values.certificate.dnsNames }} -DNS names: -{{- range .Values.certificate.dnsNames }} - - {{ . }} -{{- end }} -{{- else }} -WARNING: certificate.dnsNames is empty - the Certificate will have no SANs. -Set certificate.dnsNames to the FQDN(s) of your cache. -{{- end }} +DNS names: {{ .Values.serverHostname }}{{- range .Values.certificate.dnsNames }}, {{ . }}{{- end }} {{- end }} Check deployment status: diff --git a/templates/certificate.yaml b/templates/certificate.yaml index cfb97db..25e9c1c 100644 --- a/templates/certificate.yaml +++ b/templates/certificate.yaml @@ -1,4 +1,5 @@ {{- if .Values.certificate.enabled }} +{{- $dnsNames := concat (list (required "serverHostname is required" .Values.serverHostname)) (.Values.certificate.dnsNames | default (list)) | uniq }} apiVersion: cert-manager.io/v1 kind: Certificate metadata: @@ -6,10 +7,8 @@ metadata: labels: {{- include "pelican-cache.labels" . | nindent 4 }} spec: - {{- if .Values.certificate.dnsNames }} dnsNames: - {{- toYaml .Values.certificate.dnsNames | nindent 4 }} - {{- end }} + {{- toYaml $dnsNames | nindent 4 }} issuerRef: name: {{ .Values.certificate.issuerRef.name }} kind: {{ .Values.certificate.issuerRef.kind }} diff --git a/values.yaml b/values.yaml index ec9f73f..c98b71b 100644 --- a/values.yaml +++ b/values.yaml @@ -87,7 +87,7 @@ certificate: name: letsencrypt-prod # -- Kind: ClusterIssuer or Issuer kind: ClusterIssuer - # -- DNS Subject Alternative Names for the certificate + # -- Additional DNS Subject Alternative Names for the certificate (serverHostname is always included) dnsNames: [] # -- Use an existing TLS Secret instead of cert-manager From 3a02f6a9135e4ea0178d847aa466310157d3ee0c Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Wed, 4 Mar 2026 18:50:50 -0600 Subject: [PATCH 04/37] Don't use deprecated IssuerKey param Do not specify IssuerKey in configmap-pelican.yaml. Instead, if the issuer key is of type "existingSecret", mount the key at "/etc/pelican/issuer-keys/issuer.pem". If the type is "pvc", mount it at "/etc/pelican/issuer-keys". Co-authored-by: Copilot --- AGENTS.md | 1 - templates/_helpers.tpl | 11 ----------- templates/configmap-pelican.yaml | 2 -- templates/deployment.yaml | 4 ++-- 4 files changed, 2 insertions(+), 16 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ffb0fe4..90708c8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -39,7 +39,6 @@ pelican-cache/ │ # - pelican-cache.fullname │ # - pelican-cache.labels / selectorLabels │ # - pelican-cache.tlsSecretName - │ # - pelican-cache.issuerKeyPath │ # - pelican-cache.instanceConfig (generates 50-instance.yaml) ├── deployment.yaml # Deployment: 2-3 containers, conditional volumes ├── service.yaml # LoadBalancer Service diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index 9af7882..49bfc84 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -63,17 +63,6 @@ TLS Secret name - either from cert-manager Certificate or an existing Secret {{- end }} {{- end }} -{{/* -IssuerKey path based on namespaceKey.type -*/}} -{{- define "pelican-cache.issuerKeyPath" -}} -{{- if eq .Values.namespaceKey.type "existingSecret" -}} -/etc/pelican/issuer-keys/{{ .Values.namespaceKey.secretKey }} -{{- else -}} -/etc/pelican/jwks/issuer.jwk -{{- end -}} -{{- end }} - {{/* Generate the Pelican instance configuration (50-instance.yaml content). This is the user-configurable layer that Pelican loads from /etc/pelican/config.d/. diff --git a/templates/configmap-pelican.yaml b/templates/configmap-pelican.yaml index 5bbb81c..87caf51 100644 --- a/templates/configmap-pelican.yaml +++ b/templates/configmap-pelican.yaml @@ -12,8 +12,6 @@ data: - "/usr/share/pelican/config.d" - "/etc/pelican/config.d" - IssuerKey: {{ include "pelican-cache.issuerKeyPath" . | quote }} - Cache: StorageLocation: /cache Port: 8443 diff --git a/templates/deployment.yaml b/templates/deployment.yaml index 438c91c..8f52a17 100644 --- a/templates/deployment.yaml +++ b/templates/deployment.yaml @@ -77,11 +77,11 @@ spec: {{- if eq .Values.namespaceKey.type "existingSecret" }} - name: namespace-key readOnly: true - mountPath: /etc/pelican/issuer-keys/{{ .Values.namespaceKey.secretKey }} + mountPath: /etc/pelican/issuer-keys/issuer.pem subPath: {{ .Values.namespaceKey.secretKey }} {{- else }} - name: namespace-key - mountPath: /etc/pelican/jwks + mountPath: /etc/pelican/issuer-keys {{- end }} - name: tls-cert {{/* We cannot mount the host cert secret at /etc/pelican/certificates From 34669d769b6333571678fe5e7c2e27a3425d51b3 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Thu, 5 Mar 2026 11:15:28 -0600 Subject: [PATCH 05/37] Do not create a PVC if a PVC by that name already exists, and do not delete PVCs on uninstall Co-authored-by: Copilot --- templates/_helpers.tpl | 29 +++++++++++++++++++++++++++++ templates/pvc-cache-data.yaml | 8 ++++++-- templates/pvc-logging.yaml | 8 +++++++- templates/pvc-lotman.yaml | 8 ++++++-- templates/pvc-namespace-key.yaml | 8 ++++++-- 5 files changed, 54 insertions(+), 7 deletions(-) diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index 49bfc84..200b55c 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -142,3 +142,32 @@ XrootD.ConfigFile: /etc/pelican/xrootd.conf {{ toYaml .Values.extraPelicanConfig }} {{- end }} {{- end }} + +{{/* +Return true when a PVC should be rendered by this chart. + +Behavior: +- If the PVC does not exist yet, render it. +- If the PVC already exists and is managed by this same Helm release, render it + so upgrades can continue to manage metadata. +- Otherwise, skip rendering to avoid creation/adoption conflicts. +*/}} +{{- define "pelican-cache.shouldRenderPvc" -}} +{{- $root := .root -}} +{{- $pvcName := .pvcName -}} +{{- $existing := lookup "v1" "PersistentVolumeClaim" $root.Release.Namespace $pvcName -}} +{{- if not $existing -}} +true +{{- else -}} +{{- $annotations := default (dict) $existing.metadata.annotations -}} +{{- $labels := default (dict) $existing.metadata.labels -}} +{{- if and + (eq (index $annotations "meta.helm.sh/release-name") $root.Release.Name) + (eq (index $annotations "meta.helm.sh/release-namespace") $root.Release.Namespace) + (eq (index $labels "app.kubernetes.io/managed-by") "Helm") -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} +{{- end }} diff --git a/templates/pvc-cache-data.yaml b/templates/pvc-cache-data.yaml index f116c2f..d36f2be 100644 --- a/templates/pvc-cache-data.yaml +++ b/templates/pvc-cache-data.yaml @@ -1,8 +1,12 @@ -{{- if eq .Values.cache.storageType "pvc" }} +{{- $pvcName := printf "%s-data" (include "pelican-cache.fullname" .) -}} +{{- $shouldRender := eq (include "pelican-cache.shouldRenderPvc" (dict "root" . "pvcName" $pvcName) | trim) "true" -}} +{{- if and (eq .Values.cache.storageType "pvc") $shouldRender }} apiVersion: v1 kind: PersistentVolumeClaim metadata: - name: {{ include "pelican-cache.fullname" . }}-data + name: {{ $pvcName }} + annotations: + helm.sh/resource-policy: keep labels: {{- include "pelican-cache.labels" . | nindent 4 }} spec: diff --git a/templates/pvc-logging.yaml b/templates/pvc-logging.yaml index 51c83c4..a05e842 100644 --- a/templates/pvc-logging.yaml +++ b/templates/pvc-logging.yaml @@ -1,7 +1,12 @@ +{{- $pvcName := printf "%s-logging" (include "pelican-cache.fullname" .) -}} +{{- $shouldRender := eq (include "pelican-cache.shouldRenderPvc" (dict "root" . "pvcName" $pvcName) | trim) "true" -}} +{{- if $shouldRender }} apiVersion: v1 kind: PersistentVolumeClaim metadata: - name: {{ include "pelican-cache.fullname" . }}-logging + name: {{ $pvcName }} + annotations: + helm.sh/resource-policy: keep labels: {{- include "pelican-cache.labels" . | nindent 4 }} spec: @@ -13,3 +18,4 @@ spec: resources: requests: storage: {{ .Values.logging.pvcSize }} +{{- end }} diff --git a/templates/pvc-lotman.yaml b/templates/pvc-lotman.yaml index dedb396..de0854f 100644 --- a/templates/pvc-lotman.yaml +++ b/templates/pvc-lotman.yaml @@ -1,8 +1,12 @@ -{{- if .Values.lotman.enabled }} +{{- $pvcName := printf "%s-lotman" (include "pelican-cache.fullname" .) -}} +{{- $shouldRender := eq (include "pelican-cache.shouldRenderPvc" (dict "root" . "pvcName" $pvcName) | trim) "true" -}} +{{- if and .Values.lotman.enabled $shouldRender }} apiVersion: v1 kind: PersistentVolumeClaim metadata: - name: {{ include "pelican-cache.fullname" . }}-lotman + name: {{ $pvcName }} + annotations: + helm.sh/resource-policy: keep labels: {{- include "pelican-cache.labels" . | nindent 4 }} spec: diff --git a/templates/pvc-namespace-key.yaml b/templates/pvc-namespace-key.yaml index 855b8c0..3e5e704 100644 --- a/templates/pvc-namespace-key.yaml +++ b/templates/pvc-namespace-key.yaml @@ -1,8 +1,12 @@ -{{- if eq .Values.namespaceKey.type "pvc" }} +{{- $pvcName := printf "%s-namespace-key" (include "pelican-cache.fullname" .) -}} +{{- $shouldRender := eq (include "pelican-cache.shouldRenderPvc" (dict "root" . "pvcName" $pvcName) | trim) "true" -}} +{{- if and (eq .Values.namespaceKey.type "pvc") $shouldRender }} apiVersion: v1 kind: PersistentVolumeClaim metadata: - name: {{ include "pelican-cache.fullname" . }}-namespace-key + name: {{ $pvcName }} + annotations: + helm.sh/resource-policy: keep labels: {{- include "pelican-cache.labels" . | nindent 4 }} spec: From d665f51863692f6f289cb1268a9ffb664406f73d Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Wed, 25 Mar 2026 19:58:59 -0500 Subject: [PATCH 06/37] Add check to make sure federation label matches DiscoveryUrl The two federations we want this check for are osdf and osdf-itb, with https://osg-htc.org and https://osdf-itb.osg-htc.org as their discoveryUrls, respectively. Co-authored-by: Copilot --- ci/itb-osdf-pelican-cache-values.yaml | 4 +-- templates/_helpers.tpl | 41 +++++++++++++++++++++++++++ templates/validate.yaml | 5 ++++ values.yaml | 2 +- 4 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 templates/validate.yaml diff --git a/ci/itb-osdf-pelican-cache-values.yaml b/ci/itb-osdf-pelican-cache-values.yaml index cce544e..ff60dbd 100644 --- a/ci/itb-osdf-pelican-cache-values.yaml +++ b/ci/itb-osdf-pelican-cache-values.yaml @@ -4,8 +4,8 @@ serverHostname: "itb-osdf-pelican-cache.osdf-dev.chtc.io" federation: - discoveryUrl: "https://discovery.osdf-dev.chtc.io" - label: osdf + discoveryUrl: "https://osdf-itb.osg-htc.org" + label: osdf-itb image: repository: hub.opensciencegrid.org/pelican_platform/osdf-cache diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index 200b55c..ab0ca06 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -68,6 +68,7 @@ Generate the Pelican instance configuration (50-instance.yaml content). This is the user-configurable layer that Pelican loads from /etc/pelican/config.d/. */}} {{- define "pelican-cache.instanceConfig" -}} +{{- include "pelican-cache.validateFederation" . }} --- Federation: DiscoveryUrl: {{ .Values.federation.discoveryUrl | quote }} @@ -171,3 +172,43 @@ false {{- end -}} {{- end -}} {{- end }} + +{{/* +Validate that federation.discoveryUrl and federation.label are consistent. +The two known federations each require a specific pairing: + - https://osg-htc.org <-> osdf + - https://osdf-itb.osg-htc.org <-> osdf-itb +If either value in a pair is set to one of the known values, the other must +match exactly. +*/}} +{{- define "pelican-cache.validateFederation" -}} +{{- $url := .Values.federation.discoveryUrl | default "" }} +{{- $label := .Values.federation.label | default "" }} + +{{- /* url → required label */}} +{{- if eq $url "https://osg-htc.org" }} + {{- if and $label (ne $label "osdf") }} + {{- fail (printf "federation.discoveryUrl is %q but federation.label is %q — it must be \"osdf\"" $url $label) }} + {{- end }} +{{- end }} + +{{- if eq $url "https://osdf-itb.osg-htc.org" }} + {{- if and $label (ne $label "osdf-itb") }} + {{- fail (printf "federation.discoveryUrl is %q but federation.label is %q — it must be \"osdf-itb\"" $url $label) }} + {{- end }} +{{- end }} + +{{- /* label → required url */}} +{{- if eq $label "osdf" }} + {{- if and $url (ne $url "https://osg-htc.org") }} + {{- fail (printf "federation.label is %q but federation.discoveryUrl is %q — it must be \"https://osg-htc.org\"" $label $url) }} + {{- end }} +{{- end }} + +{{- if eq $label "osdf-itb" }} + {{- if and $url (ne $url "https://osdf-itb.osg-htc.org") }} + {{- fail (printf "federation.label is %q but federation.discoveryUrl is %q — it must be \"https://osdf-itb.osg-htc.org\"" $label $url) }} + {{- end }} +{{- end }} +{{- end }} + diff --git a/templates/validate.yaml b/templates/validate.yaml new file mode 100644 index 0000000..72f2a80 --- /dev/null +++ b/templates/validate.yaml @@ -0,0 +1,5 @@ +{{/* +This template exists solely to trigger validation checks on every render. +It produces no Kubernetes resources — the file is empty after rendering. +*/}} +{{- include "pelican-cache.validateFederation" . }} diff --git a/values.yaml b/values.yaml index c98b71b..fa6f7f4 100644 --- a/values.yaml +++ b/values.yaml @@ -14,7 +14,7 @@ federation: # -- Discovery URL for the federation (OSDF default) discoveryUrl: "https://osg-htc.org" # -- Federation label applied to all resources (e.g. "osdf") - label: "" + label: "osdf" # -- Cache container image image: From 7e4d5664a94ef11be9e1665180377e23a6409546 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Fri, 20 Mar 2026 11:18:17 -0500 Subject: [PATCH 07/37] Overhaul storage definitions * Specify the type of storage for a cache by `cache.type`; if `cache.type==pvc`, then the specifics go under `cache.pvc`; if `cache.type==hostPath`, then the specifics go under `cache.hostPath`. * Rename storageClassName to storageClass since that's more common. * Make sure specifying volumes are consistent between the `cache` section, `logging` section and `lotman` section. * Rename "namespaceKey" to "issuerKey" and the default secret key name to "private-key.pem"; "issuer key" is the terminology Pelican uses and "namespace key" doesn't make much sense for a cache; private-key.pem is the name of the file that `pelican key create` creates. * Add to NOTES.txt a list of all the PVCs we created; also mention that we do not delete them on uninstall. * Add validation for PVCs and volumes. * If `cache.type==pvc`, require `cache.pvc.existingClaim` or `cache.pvc.storageClass`. * If `cache.type==hostPath`, require `cache.hostPath.path`. * If `issuerKey.type==pvc`, require `issuerKey.pvc.storageClass`. * If `issuerKey.type==existingSecret`, require `issuerKey.existingSecret`. * If lotman is enabled, require `lotman.pvc.existingClaim` or `lotman.pvc.storageClass`. * If oidc is enabled, require `oidc.existingSecret`. * Require `webPasswordSecret`. Co-authored-by: Copilot --- ci/houston2-i2-pelican-cache-values.yaml | 9 +-- ci/itb-osdf-pelican-cache-values.yaml | 14 ++-- ci/uw-osdf-cache-values.yaml | 17 +++-- templates/NOTES.txt | 42 ++++++++++++ templates/_helpers.tpl | 64 +++++++++++++++++++ templates/deployment.yaml | 52 ++++++++++----- templates/pvc-cache-data.yaml | 8 +-- ...namespace-key.yaml => pvc-issuer-key.yaml} | 10 +-- templates/pvc-logging.yaml | 12 ++-- templates/pvc-lotman.yaml | 8 +-- templates/validate.yaml | 1 + values.yaml | 55 ++++++++++------ 12 files changed, 223 insertions(+), 69 deletions(-) rename templates/{pvc-namespace-key.yaml => pvc-issuer-key.yaml} (54%) diff --git a/ci/houston2-i2-pelican-cache-values.yaml b/ci/houston2-i2-pelican-cache-values.yaml index 8210ff9..81fbcab 100644 --- a/ci/houston2-i2-pelican-cache-values.yaml +++ b/ci/houston2-i2-pelican-cache-values.yaml @@ -17,11 +17,12 @@ cvmfsRedirector: # The real deployment uses an existing PVC (pvc-dtn-pas.hous.net.internet2.edu). cache: - storageType: existingPVC - existingPVC: pvc-dtn-pas.hous.net.internet2.edu + type: hostPath + hostPath: + path: /cache concurrency: 160 -namespaceKey: +issuerKey: type: existingSecret existingSecret: houston2-i2-pelican-pelican-cache-key secretKey: issuer.pem @@ -59,7 +60,7 @@ resources: # Logs go to the cache volume (/cache/pelican/), no separate logging PVC. logging: - enabled: false + persist: false level: INFO cache: Pss: warn diff --git a/ci/itb-osdf-pelican-cache-values.yaml b/ci/itb-osdf-pelican-cache-values.yaml index ff60dbd..e3fc1c4 100644 --- a/ci/itb-osdf-pelican-cache-values.yaml +++ b/ci/itb-osdf-pelican-cache-values.yaml @@ -15,12 +15,13 @@ cvmfsRedirector: enabled: false cache: - storageType: pvc - storageClassName: 3x-replica-hdd-raddus - pvcSize: 100Gi + type: pvc + pvc: + storageClass: 3x-replica-hdd-raddus + size: 100Gi concurrency: 40 -namespaceKey: +issuerKey: type: existingSecret existingSecret: itb-osdf-pelican-cache-issuer-keys secretKey: issuer.pem @@ -47,8 +48,9 @@ resources: logging: level: "debug" - storageClassName: 3x-replica-block - pvcSize: 10Gi + pvc: + storageClass: 3x-replica-block + size: 10Gi cache: Scitokens: debug Pss: debug diff --git a/ci/uw-osdf-cache-values.yaml b/ci/uw-osdf-cache-values.yaml index d321054..0751096 100644 --- a/ci/uw-osdf-cache-values.yaml +++ b/ci/uw-osdf-cache-values.yaml @@ -15,8 +15,9 @@ cvmfsRedirector: enabled: false cache: - storageType: hostPath - hostPath: /srv/pelican-cache/ + type: hostPath + hostPath: + path: /srv/pelican-cache/ blocksToPrefetch: 10 concurrency: 240 highWaterMark: 27000g @@ -25,7 +26,7 @@ cache: filesNominalSize: 17000g filesBaseSize: 16500g -namespaceKey: +issuerKey: type: existingSecret existingSecret: uw-osdf-cache-issuer-key secretKey: issuer.pem @@ -61,8 +62,9 @@ resources: logging: level: "debug" - storageClassName: 3x-replica-hdd-raddus - pvcSize: 50Gi + pvc: + storageClass: 3x-replica-hdd-raddus + size: 50Gi cache: Pss: "debug" Pfc: "debug" @@ -70,8 +72,9 @@ logging: lotman: enabled: true - storageClassName: 3x-replica-hdd-raddus - pvcSize: 10Gi + pvc: + storageClass: 3x-replica-hdd-raddus + size: 10Gi oidc: enabled: true diff --git a/templates/NOTES.txt b/templates/NOTES.txt index 0fc6ea2..360d9a6 100644 --- a/templates/NOTES.txt +++ b/templates/NOTES.txt @@ -20,3 +20,45 @@ View logs: kubectl -n {{ .Release.Namespace }} logs -l app.kubernetes.io/instance={{ .Release.Name }} -c pelican-cache For more information: https://docs.pelicanplatform.org/ + +{{- $fullname := include "pelican-cache.fullname" . }} +{{- $loggingPersist := true -}} +{{- if hasKey .Values.logging "persist" -}} +{{- $loggingPersist = .Values.logging.persist -}} +{{- end -}} + +{{- $createdPVCs := list -}} + +{{- $cachePvcName := printf "%s-data" $fullname -}} +{{- $cacheShouldRender := eq (include "pelican-cache.shouldRenderPvc" (dict "root" . "pvcName" $cachePvcName) | trim) "true" -}} +{{- if and (eq .Values.cache.type "pvc") (not .Values.cache.pvc.existingClaim) $cacheShouldRender -}} +{{- $createdPVCs = append $createdPVCs $cachePvcName -}} +{{- end -}} + +{{- $loggingPvcName := printf "%s-logging" $fullname -}} +{{- $loggingShouldRender := eq (include "pelican-cache.shouldRenderPvc" (dict "root" . "pvcName" $loggingPvcName) | trim) "true" -}} +{{- if and $loggingPersist (not .Values.logging.pvc.existingClaim) $loggingShouldRender -}} +{{- $createdPVCs = append $createdPVCs $loggingPvcName -}} +{{- end -}} + +{{- $issuerKeyPvcName := printf "%s-issuer-key" $fullname -}} +{{- $issuerKeyShouldRender := eq (include "pelican-cache.shouldRenderPvc" (dict "root" . "pvcName" $issuerKeyPvcName) | trim) "true" -}} +{{- if and (eq .Values.issuerKey.type "pvc") $issuerKeyShouldRender -}} +{{- $createdPVCs = append $createdPVCs $issuerKeyPvcName -}} +{{- end -}} + +{{- $lotmanPvcName := printf "%s-lotman" $fullname -}} +{{- $lotmanShouldRender := eq (include "pelican-cache.shouldRenderPvc" (dict "root" . "pvcName" $lotmanPvcName) | trim) "true" -}} +{{- if and .Values.lotman.enabled (not .Values.lotman.pvc.existingClaim) $lotmanShouldRender -}} +{{- $createdPVCs = append $createdPVCs $lotmanPvcName -}} +{{- end -}} + +{{- if gt (len $createdPVCs) 0 }} + +This chart created the following PVCs: +{{- range $createdPVCs }} + - {{ . }} +{{- end }} + +These PVCs are annotated with helm.sh/resource-policy=keep and will NOT be deleted on uninstall. +{{- end }} diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index ab0ca06..0e12a6a 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -212,3 +212,67 @@ match exactly. {{- end }} {{- end }} +{{/* +Validate required values and conditional requirements. +*/}} +{{- define "pelican-cache.validateRequiredValues" -}} +{{- $cacheStorageType := .Values.cache.type | default "" }} +{{- $issuerKeyType := .Values.issuerKey.type | default "" }} +{{- $loggingPersist := true }} +{{- if hasKey .Values.logging "persist" }} + {{- $loggingPersist = .Values.logging.persist }} +{{- end }} + +{{- if eq $cacheStorageType "pvc" }} + {{- if not .Values.cache.pvc.existingClaim }} + {{- if eq (trim (default "" .Values.cache.pvc.storageClass)) "" }} + {{- fail "cache.pvc.storageClass must be nonempty when cache.type is \"pvc\" and cache.pvc.existingClaim is not set" }} + {{- end }} + {{- end }} +{{- end }} + +{{- if eq $cacheStorageType "hostPath" }} + {{- if eq (trim (default "" .Values.cache.hostPath.path)) "" }} + {{- fail "cache.hostPath.path must be nonempty when cache.type is \"hostPath\"" }} + {{- end }} +{{- end }} + +{{- if eq $issuerKeyType "pvc" }} + {{- if eq (trim (default "" .Values.issuerKey.pvc.storageClass)) "" }} + {{- fail "issuerKey.pvc.storageClass must be nonempty when issuerKey.type is \"pvc\"" }} + {{- end }} +{{- end }} + +{{- if eq $issuerKeyType "existingSecret" }} + {{- if eq (trim (default "" .Values.issuerKey.existingSecret)) "" }} + {{- fail "issuerKey.existingSecret must be nonempty when issuerKey.type is \"existingSecret\"" }} + {{- end }} +{{- end }} + +{{- if $loggingPersist }} + {{- if not .Values.logging.pvc.existingClaim }} + {{- if eq (trim (default "" .Values.logging.pvc.storageClass)) "" }} + {{- fail "logging.pvc.storageClass must be nonempty when logging.persist is true and logging.pvc.existingClaim is not set" }} + {{- end }} + {{- end }} +{{- end }} + +{{- if .Values.lotman.enabled }} + {{- if not .Values.lotman.pvc.existingClaim }} + {{- if eq (trim (default "" .Values.lotman.pvc.storageClass)) "" }} + {{- fail "lotman.pvc.storageClass must be nonempty when lotman.enabled is true and lotman.pvc.existingClaim is not set" }} + {{- end }} + {{- end }} +{{- end }} + +{{- if .Values.oidc.enabled }} + {{- if eq (trim (default "" .Values.oidc.existingSecret)) "" }} + {{- fail "oidc.existingSecret must be nonempty when oidc.enabled is true" }} + {{- end }} +{{- end }} + +{{- if eq (trim (default "" .Values.webPasswordSecret)) "" }} + {{- fail "webPasswordSecret must be nonempty" }} +{{- end }} +{{- end }} + diff --git a/templates/deployment.yaml b/templates/deployment.yaml index 8f52a17..18f910a 100644 --- a/templates/deployment.yaml +++ b/templates/deployment.yaml @@ -72,15 +72,21 @@ spec: subPath: 50-instance.yaml - name: data mountPath: /cache + {{- $loggingPersist := true }} + {{- if hasKey .Values.logging "persist" }} + {{- $loggingPersist = .Values.logging.persist }} + {{- end }} + {{- if $loggingPersist }} - name: logging-volume mountPath: /var/log/pelican - {{- if eq .Values.namespaceKey.type "existingSecret" }} - - name: namespace-key + {{- end }} + {{- if eq .Values.issuerKey.type "existingSecret" }} + - name: issuer-key readOnly: true mountPath: /etc/pelican/issuer-keys/issuer.pem - subPath: {{ .Values.namespaceKey.secretKey }} + subPath: {{ .Values.issuerKey.secretKey }} {{- else }} - - name: namespace-key + - name: issuer-key mountPath: /etc/pelican/issuer-keys {{- end }} - name: tls-cert @@ -140,8 +146,14 @@ its own CA there. So we use a different directory here and in the Pelican confi {{- toYaml . | nindent 12 }} {{- end }} volumeMounts: + {{- $loggingPersist := true }} + {{- if hasKey .Values.logging "persist" }} + {{- $loggingPersist = .Values.logging.persist }} + {{- end }} + {{- if $loggingPersist }} - name: logging-volume mountPath: /var/log/pelican + {{- end }} - name: log-rotate-config mountPath: /etc/logrotate.d/ @@ -176,42 +188,48 @@ its own CA there. So we use a different directory here and in the Pelican confi name: {{ include "pelican-cache.fullname" . }}-logrotate # Cache data volume (PVC or hostPath) - {{- if eq .Values.cache.storageType "pvc" }} + {{- if eq .Values.cache.type "pvc" }} - name: data persistentVolumeClaim: - claimName: {{ include "pelican-cache.fullname" . }}-data + claimName: {{ if .Values.cache.pvc.existingClaim }}{{ .Values.cache.pvc.existingClaim }}{{ else }}{{ include "pelican-cache.fullname" . }}-data{{ end }} {{- else }} - name: data hostPath: - path: {{ required "cache.hostPath is required when cache.storageType is hostPath" .Values.cache.hostPath }} + path: {{ required "cache.hostPath.path is required when cache.type is hostPath" .Values.cache.hostPath.path }} type: Directory {{- end }} + {{- $loggingPersist := true }} + {{- if hasKey .Values.logging "persist" }} + {{- $loggingPersist = .Values.logging.persist }} + {{- end }} + {{- if $loggingPersist }} # Logging volume - name: logging-volume persistentVolumeClaim: - claimName: {{ include "pelican-cache.fullname" . }}-logging + claimName: {{ if .Values.logging.pvc.existingClaim }}{{ .Values.logging.pvc.existingClaim }}{{ else }}{{ include "pelican-cache.fullname" . }}-logging{{ end }} + {{- end }} - # Namespace / issuer key - {{- if eq .Values.namespaceKey.type "existingSecret" }} - - name: namespace-key + # Issuer key + {{- if eq .Values.issuerKey.type "existingSecret" }} + - name: issuer-key secret: - secretName: {{ required "namespaceKey.existingSecret is required when type is existingSecret" .Values.namespaceKey.existingSecret }} + secretName: {{ required "issuerKey.existingSecret is required when type is existingSecret" .Values.issuerKey.existingSecret }} items: - - key: {{ .Values.namespaceKey.secretKey }} - path: {{ .Values.namespaceKey.secretKey }} + - key: {{ .Values.issuerKey.secretKey }} + path: {{ .Values.issuerKey.secretKey }} mode: 0600 {{- else }} - - name: namespace-key + - name: issuer-key persistentVolumeClaim: - claimName: {{ include "pelican-cache.fullname" . }}-namespace-key + claimName: {{ include "pelican-cache.fullname" . }}-issuer-key {{- end }} {{- if .Values.lotman.enabled }} # Lotman persistent data - name: lotman-data persistentVolumeClaim: - claimName: {{ include "pelican-cache.fullname" . }}-lotman + claimName: {{ if .Values.lotman.pvc.existingClaim }}{{ .Values.lotman.pvc.existingClaim }}{{ else }}{{ include "pelican-cache.fullname" . }}-lotman{{ end }} {{- end }} {{- if .Values.oidc.enabled }} diff --git a/templates/pvc-cache-data.yaml b/templates/pvc-cache-data.yaml index d36f2be..ddedcc9 100644 --- a/templates/pvc-cache-data.yaml +++ b/templates/pvc-cache-data.yaml @@ -1,6 +1,6 @@ {{- $pvcName := printf "%s-data" (include "pelican-cache.fullname" .) -}} {{- $shouldRender := eq (include "pelican-cache.shouldRenderPvc" (dict "root" . "pvcName" $pvcName) | trim) "true" -}} -{{- if and (eq .Values.cache.storageType "pvc") $shouldRender }} +{{- if and (eq .Values.cache.type "pvc") (not .Values.cache.pvc.existingClaim) $shouldRender }} apiVersion: v1 kind: PersistentVolumeClaim metadata: @@ -10,12 +10,12 @@ metadata: labels: {{- include "pelican-cache.labels" . | nindent 4 }} spec: - {{- if .Values.cache.storageClassName }} - storageClassName: {{ .Values.cache.storageClassName | quote }} + {{- if .Values.cache.pvc.storageClass }} + storageClassName: {{ .Values.cache.pvc.storageClass | quote }} {{- end }} accessModes: - ReadWriteOnce resources: requests: - storage: {{ .Values.cache.pvcSize }} + storage: {{ .Values.cache.pvc.size }} {{- end }} diff --git a/templates/pvc-namespace-key.yaml b/templates/pvc-issuer-key.yaml similarity index 54% rename from templates/pvc-namespace-key.yaml rename to templates/pvc-issuer-key.yaml index 3e5e704..221d54b 100644 --- a/templates/pvc-namespace-key.yaml +++ b/templates/pvc-issuer-key.yaml @@ -1,6 +1,6 @@ -{{- $pvcName := printf "%s-namespace-key" (include "pelican-cache.fullname" .) -}} +{{- $pvcName := printf "%s-issuer-key" (include "pelican-cache.fullname" .) -}} {{- $shouldRender := eq (include "pelican-cache.shouldRenderPvc" (dict "root" . "pvcName" $pvcName) | trim) "true" -}} -{{- if and (eq .Values.namespaceKey.type "pvc") $shouldRender }} +{{- if and (eq .Values.issuerKey.type "pvc") $shouldRender }} apiVersion: v1 kind: PersistentVolumeClaim metadata: @@ -10,12 +10,12 @@ metadata: labels: {{- include "pelican-cache.labels" . | nindent 4 }} spec: - {{- if .Values.namespaceKey.pvc.storageClassName }} - storageClassName: {{ .Values.namespaceKey.pvc.storageClassName | quote }} + {{- if .Values.issuerKey.pvc.storageClass }} + storageClassName: {{ .Values.issuerKey.pvc.storageClass | quote }} {{- end }} accessModes: - ReadWriteOnce resources: requests: - storage: {{ .Values.namespaceKey.pvc.size }} + storage: {{ .Values.issuerKey.pvc.size }} {{- end }} diff --git a/templates/pvc-logging.yaml b/templates/pvc-logging.yaml index a05e842..366b68b 100644 --- a/templates/pvc-logging.yaml +++ b/templates/pvc-logging.yaml @@ -1,6 +1,10 @@ {{- $pvcName := printf "%s-logging" (include "pelican-cache.fullname" .) -}} {{- $shouldRender := eq (include "pelican-cache.shouldRenderPvc" (dict "root" . "pvcName" $pvcName) | trim) "true" -}} -{{- if $shouldRender }} +{{- $loggingPersist := true -}} +{{- if hasKey .Values.logging "persist" -}} +{{- $loggingPersist = .Values.logging.persist -}} +{{- end -}} +{{- if and $loggingPersist (not .Values.logging.pvc.existingClaim) $shouldRender }} apiVersion: v1 kind: PersistentVolumeClaim metadata: @@ -10,12 +14,12 @@ metadata: labels: {{- include "pelican-cache.labels" . | nindent 4 }} spec: - {{- if .Values.logging.storageClassName }} - storageClassName: {{ .Values.logging.storageClassName | quote }} + {{- if .Values.logging.pvc.storageClass }} + storageClassName: {{ .Values.logging.pvc.storageClass | quote }} {{- end }} accessModes: - ReadWriteOnce resources: requests: - storage: {{ .Values.logging.pvcSize }} + storage: {{ .Values.logging.pvc.size }} {{- end }} diff --git a/templates/pvc-lotman.yaml b/templates/pvc-lotman.yaml index de0854f..c151375 100644 --- a/templates/pvc-lotman.yaml +++ b/templates/pvc-lotman.yaml @@ -1,6 +1,6 @@ {{- $pvcName := printf "%s-lotman" (include "pelican-cache.fullname" .) -}} {{- $shouldRender := eq (include "pelican-cache.shouldRenderPvc" (dict "root" . "pvcName" $pvcName) | trim) "true" -}} -{{- if and .Values.lotman.enabled $shouldRender }} +{{- if and .Values.lotman.enabled (not .Values.lotman.pvc.existingClaim) $shouldRender }} apiVersion: v1 kind: PersistentVolumeClaim metadata: @@ -10,12 +10,12 @@ metadata: labels: {{- include "pelican-cache.labels" . | nindent 4 }} spec: - {{- if .Values.lotman.storageClassName }} - storageClassName: {{ .Values.lotman.storageClassName | quote }} + {{- if .Values.lotman.pvc.storageClass }} + storageClassName: {{ .Values.lotman.pvc.storageClass | quote }} {{- end }} accessModes: - ReadWriteOnce resources: requests: - storage: {{ .Values.lotman.pvcSize }} + storage: {{ .Values.lotman.pvc.size }} {{- end }} diff --git a/templates/validate.yaml b/templates/validate.yaml index 72f2a80..c0bac3e 100644 --- a/templates/validate.yaml +++ b/templates/validate.yaml @@ -3,3 +3,4 @@ This template exists solely to trigger validation checks on every render. It produces no Kubernetes resources — the file is empty after rendering. */}} {{- include "pelican-cache.validateFederation" . }} +{{- include "pelican-cache.validateRequiredValues" . }} diff --git a/values.yaml b/values.yaml index fa6f7f4..cbf2f3c 100644 --- a/values.yaml +++ b/values.yaml @@ -40,15 +40,23 @@ cvmfsRedirector: cpu: "1" # -- Cache data storage configuration +# -- You must choose between "pvc" and "hostPath" to determine where to +# -- persist, and then configure the details in the appropriate subsections. cache: # -- Storage type: "pvc" or "hostPath" - storageType: pvc - # -- Host path when storageType is "hostPath" (e.g. /srv/pelican-cache/) - hostPath: "" - # -- PVC storage class name when storageType is "pvc" - storageClassName: "" - # -- PVC size when storageType is "pvc" - pvcSize: 1000Gi + type: pvc + # -- Host path configuration when type is "hostPath" + hostPath: + # -- Host path (e.g. /srv/pelican-cache/) + path: "" + # -- PVC configuration when type is "pvc" + pvc: + # -- Name of an existing PVC to use (if set, storageClass and size are ignored) + existingClaim: "" + # -- Storage class name for creating a new PVC + storageClass: "" + # -- PVC size when creating a new PVC + size: 1000Gi # -- Number of blocks to prefetch (e.g. 10) blocksToPrefetch: "" # -- Maximum concurrent requests (e.g. 240) @@ -64,19 +72,19 @@ cache: # -- diskusage base file tracking size (e.g. "16500g") filesBaseSize: "" -# -- Namespace/issuer key configuration -namespaceKey: +# -- Issuer key configuration +issuerKey: # -- Key source type: "pvc" (auto-generated by Pelican) or "existingSecret" type: pvc pvc: - # -- Storage class for the namespace key PVC - storageClassName: "" + # -- Storage class for the issuer key PVC + storageClass: "" # -- PVC size size: 10Mi # -- Name of an existing Secret containing the issuer key (when type is "existingSecret") existingSecret: "" # -- Key within the Secret that contains the issuer key file - secretKey: "issuer.pem" + secretKey: "private-key.pem" # -- TLS certificate configuration (via cert-manager) certificate: @@ -120,12 +128,18 @@ resources: # -- Logging configuration logging: + # -- Set to false to disable persisted logging (no logging PVC and no logging volume mounts). + persist: true # -- Global log level level: INFO - # -- Storage class for logging PVC - storageClassName: "" - # -- Logging PVC size - pvcSize: 5Gi + # -- PVC configuration for persisted logs (used when persist=true) + pvc: + # -- Name of an existing PVC to use for logs (if set, storageClass and size are ignored) + existingClaim: "" + # -- Storage class for creating a new logging PVC + storageClass: "" + # -- Logging PVC size when creating a new PVC + size: 50Gi # -- Per-subsystem cache log levels cache: {} # Pss: debug @@ -138,8 +152,13 @@ logging: # -- Lotman (lot-based storage management) lotman: enabled: false - storageClassName: "" - pvcSize: 10Gi + pvc: + # -- Name of an existing PVC to use for lotman data (if set, storageClass and size are ignored) + existingClaim: "" + # -- Storage class for creating a new lotman PVC + storageClass: "" + # -- Lotman PVC size when creating a new PVC + size: 10Gi # -- OIDC authentication oidc: From a9b0d27aa66841332b5f9651d17cb9a9bd23dff3 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Thu, 26 Mar 2026 13:02:55 -0500 Subject: [PATCH 08/37] Fix a variety of logging issues and inconsistencies * There were multiple top-level blocks regarding logging and log rotation (for example, resources were in one place, the images were in another); these have been consolidated. * Renamed logging.persist to logging.persistence and put the various options under it. Instead of logging.persistence.enabled, we have logging.persistence.separateVolume because we always persist logs, it's just that on the NRP caches we put the logs on the cache volume. * If we're not logging to a separate volume, mount the cache data volume at /var/log (expecting the logs to live in pelican/*.log under the data volume), following what the Houston I2 cache does. NOTE: This is different than what some of the OSStore Origins do (they mount a subPath of the data volume as /var/log/pelican) so we will have to see if those two patterns can be consolidated. * Fix a brittle hasKey check in the deployment template. Co-authored-by: Copilot --- ci/houston2-i2-pelican-cache-values.yaml | 7 ++-- ci/itb-osdf-pelican-cache-values.yaml | 3 +- ci/uw-osdf-cache-values.yaml | 7 ++-- templates/NOTES.txt | 7 ++-- templates/_helpers.tpl | 11 +++--- templates/deployment.yaml | 31 +++++++---------- templates/pvc-logging.yaml | 13 +++---- values.yaml | 44 +++++++++++++----------- 8 files changed, 59 insertions(+), 64 deletions(-) diff --git a/ci/houston2-i2-pelican-cache-values.yaml b/ci/houston2-i2-pelican-cache-values.yaml index 81fbcab..0889d04 100644 --- a/ci/houston2-i2-pelican-cache-values.yaml +++ b/ci/houston2-i2-pelican-cache-values.yaml @@ -50,7 +50,9 @@ resources: limits: cpu: "16000m" memory: "210Gi" - logrotate: + +logrotate: + resources: requests: cpu: "1" memory: "500M" @@ -60,7 +62,8 @@ resources: # Logs go to the cache volume (/cache/pelican/), no separate logging PVC. logging: - persist: false + persistence: + separateVolume: false level: INFO cache: Pss: warn diff --git a/ci/itb-osdf-pelican-cache-values.yaml b/ci/itb-osdf-pelican-cache-values.yaml index e3fc1c4..2f40097 100644 --- a/ci/itb-osdf-pelican-cache-values.yaml +++ b/ci/itb-osdf-pelican-cache-values.yaml @@ -48,7 +48,8 @@ resources: logging: level: "debug" - pvc: + persistence: + separateVolume: true storageClass: 3x-replica-block size: 10Gi cache: diff --git a/ci/uw-osdf-cache-values.yaml b/ci/uw-osdf-cache-values.yaml index 0751096..13f14df 100644 --- a/ci/uw-osdf-cache-values.yaml +++ b/ci/uw-osdf-cache-values.yaml @@ -52,7 +52,9 @@ resources: requests: cpu: "24" memory: "48Gi" - logrotate: + +logrotate: + resources: requests: cpu: "1" memory: "500M" @@ -62,7 +64,8 @@ resources: logging: level: "debug" - pvc: + persistence: + separateVolume: true storageClass: 3x-replica-hdd-raddus size: 50Gi cache: diff --git a/templates/NOTES.txt b/templates/NOTES.txt index 360d9a6..77a1fb5 100644 --- a/templates/NOTES.txt +++ b/templates/NOTES.txt @@ -22,10 +22,7 @@ View logs: For more information: https://docs.pelicanplatform.org/ {{- $fullname := include "pelican-cache.fullname" . }} -{{- $loggingPersist := true -}} -{{- if hasKey .Values.logging "persist" -}} -{{- $loggingPersist = .Values.logging.persist -}} -{{- end -}} +{{- $loggingPersist := .Values.logging.persistence.separateVolume -}} {{- $createdPVCs := list -}} @@ -37,7 +34,7 @@ For more information: https://docs.pelicanplatform.org/ {{- $loggingPvcName := printf "%s-logging" $fullname -}} {{- $loggingShouldRender := eq (include "pelican-cache.shouldRenderPvc" (dict "root" . "pvcName" $loggingPvcName) | trim) "true" -}} -{{- if and $loggingPersist (not .Values.logging.pvc.existingClaim) $loggingShouldRender -}} +{{- if and $loggingPersist (not .Values.logging.persistence.existingClaim) $loggingShouldRender -}} {{- $createdPVCs = append $createdPVCs $loggingPvcName -}} {{- end -}} diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index 0e12a6a..dcf0ccb 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -218,10 +218,7 @@ Validate required values and conditional requirements. {{- define "pelican-cache.validateRequiredValues" -}} {{- $cacheStorageType := .Values.cache.type | default "" }} {{- $issuerKeyType := .Values.issuerKey.type | default "" }} -{{- $loggingPersist := true }} -{{- if hasKey .Values.logging "persist" }} - {{- $loggingPersist = .Values.logging.persist }} -{{- end }} +{{- $loggingPersist := .Values.logging.persistence.separateVolume }} {{- if eq $cacheStorageType "pvc" }} {{- if not .Values.cache.pvc.existingClaim }} @@ -250,9 +247,9 @@ Validate required values and conditional requirements. {{- end }} {{- if $loggingPersist }} - {{- if not .Values.logging.pvc.existingClaim }} - {{- if eq (trim (default "" .Values.logging.pvc.storageClass)) "" }} - {{- fail "logging.pvc.storageClass must be nonempty when logging.persist is true and logging.pvc.existingClaim is not set" }} + {{- if not .Values.logging.persistence.existingClaim }} + {{- if eq (trim (default "" .Values.logging.persistence.storageClass)) "" }} + {{- fail "logging.persistence.storageClass must be nonempty when logging.persistence.separateVolume is true and logging.persistence.existingClaim is not set" }} {{- end }} {{- end }} {{- end }} diff --git a/templates/deployment.yaml b/templates/deployment.yaml index 18f910a..a6bb913 100644 --- a/templates/deployment.yaml +++ b/templates/deployment.yaml @@ -72,13 +72,12 @@ spec: subPath: 50-instance.yaml - name: data mountPath: /cache - {{- $loggingPersist := true }} - {{- if hasKey .Values.logging "persist" }} - {{- $loggingPersist = .Values.logging.persist }} - {{- end }} - {{- if $loggingPersist }} + {{- if .Values.logging.persistence.separateVolume }} - name: logging-volume mountPath: /var/log/pelican + {{- else }} + - name: data + mountPath: /var/log {{- end }} {{- if eq .Values.issuerKey.type "existingSecret" }} - name: issuer-key @@ -140,19 +139,19 @@ its own CA there. So we use a different directory here and in the Pelican confi # ---- Logrotate sidecar ---- - name: logrotate - image: "{{ .Values.logrotateImage.repository }}:{{ .Values.logrotateImage.tag }}" - {{- with .Values.resources.logrotate }} + image: "{{ .Values.logrotate.image.repository }}:{{ .Values.logrotate.image.tag }}" + imagePullPolicy: Always + {{- with .Values.logrotate.resources }} resources: {{- toYaml . | nindent 12 }} {{- end }} volumeMounts: - {{- $loggingPersist := true }} - {{- if hasKey .Values.logging "persist" }} - {{- $loggingPersist = .Values.logging.persist }} - {{- end }} - {{- if $loggingPersist }} + {{- if .Values.logging.persistence.separateVolume }} - name: logging-volume mountPath: /var/log/pelican + {{- else }} + - name: data + mountPath: /var/log {{- end }} - name: log-rotate-config mountPath: /etc/logrotate.d/ @@ -199,15 +198,11 @@ its own CA there. So we use a different directory here and in the Pelican confi type: Directory {{- end }} - {{- $loggingPersist := true }} - {{- if hasKey .Values.logging "persist" }} - {{- $loggingPersist = .Values.logging.persist }} - {{- end }} - {{- if $loggingPersist }} # Logging volume + {{- if .Values.logging.persistence.separateVolume }} - name: logging-volume persistentVolumeClaim: - claimName: {{ if .Values.logging.pvc.existingClaim }}{{ .Values.logging.pvc.existingClaim }}{{ else }}{{ include "pelican-cache.fullname" . }}-logging{{ end }} + claimName: {{ if .Values.logging.persistence.existingClaim }}{{ .Values.logging.persistence.existingClaim }}{{ else }}{{ include "pelican-cache.fullname" . }}-logging{{ end }} {{- end }} # Issuer key diff --git a/templates/pvc-logging.yaml b/templates/pvc-logging.yaml index 366b68b..cdcd8fa 100644 --- a/templates/pvc-logging.yaml +++ b/templates/pvc-logging.yaml @@ -1,10 +1,7 @@ {{- $pvcName := printf "%s-logging" (include "pelican-cache.fullname" .) -}} {{- $shouldRender := eq (include "pelican-cache.shouldRenderPvc" (dict "root" . "pvcName" $pvcName) | trim) "true" -}} -{{- $loggingPersist := true -}} -{{- if hasKey .Values.logging "persist" -}} -{{- $loggingPersist = .Values.logging.persist -}} -{{- end -}} -{{- if and $loggingPersist (not .Values.logging.pvc.existingClaim) $shouldRender }} +{{- $loggingPersist := .Values.logging.persistence.separateVolume -}} +{{- if and $loggingPersist (not .Values.logging.persistence.existingClaim) $shouldRender }} apiVersion: v1 kind: PersistentVolumeClaim metadata: @@ -14,12 +11,12 @@ metadata: labels: {{- include "pelican-cache.labels" . | nindent 4 }} spec: - {{- if .Values.logging.pvc.storageClass }} - storageClassName: {{ .Values.logging.pvc.storageClass | quote }} + {{- if .Values.logging.persistence.storageClass }} + storageClassName: {{ .Values.logging.persistence.storageClass | quote }} {{- end }} accessModes: - ReadWriteOnce resources: requests: - storage: {{ .Values.logging.pvc.size }} + storage: {{ .Values.logging.persistence.size }} {{- end }} diff --git a/values.yaml b/values.yaml index cbf2f3c..da2e113 100644 --- a/values.yaml +++ b/values.yaml @@ -22,10 +22,20 @@ image: tag: "" pullPolicy: Always -# -- Logrotate sidecar image -logrotateImage: - repository: hub.opensciencegrid.org/opensciencegrid/logrotate - tag: "24-release" +# -- Logrotate sidecar configuration +logrotate: + image: + repository: hub.opensciencegrid.org/opensciencegrid/logrotate + tag: "24-release" + resources: + requests: + cpu: "1" + memory: "500M" + limits: + cpu: "2" + memory: "2G" + size: 500M + rotate: 10 # -- CVMFS port redirector sidecar cvmfsRedirector: @@ -118,28 +128,24 @@ resources: cpu: "1000m" memory: "16Gi" limits: {} - logrotate: - requests: - cpu: "1" - memory: "500M" - limits: - cpu: "2" - memory: "2G" # -- Logging configuration logging: - # -- Set to false to disable persisted logging (no logging PVC and no logging volume mounts). - persist: true - # -- Global log level - level: INFO - # -- PVC configuration for persisted logs (used when persist=true) - pvc: + persistence: + # -- Provision a dedicated PVC for the /var/log/pelican volume. + # -- When false, the data volume is also mounted at /var/log. + # -- This supports deployments that write logs to /var/log/pelican + # -- on the cache disk (e.g. via extraPelicanConfig.Logging.LogLocation). + # -- Logrotate always runs. + separateVolume: true # -- Name of an existing PVC to use for logs (if set, storageClass and size are ignored) existingClaim: "" # -- Storage class for creating a new logging PVC storageClass: "" # -- Logging PVC size when creating a new PVC size: 50Gi + # -- Global log level + level: INFO # -- Per-subsystem cache log levels cache: {} # Pss: debug @@ -225,7 +231,3 @@ extraVolumeMounts: [] # chart-managed settings so they can override them. extraPelicanConfig: {} -# -- Logrotate settings -logrotate: - size: 500M - rotate: 10 From e325b3bd477268f4d2c53b04345a23781820cf09 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Wed, 25 Mar 2026 17:24:53 -0500 Subject: [PATCH 09/37] Change the webserver password to server-web-passwd that is the default filename that `pelican generate password` creates. --- templates/_helpers.tpl | 2 +- templates/deployment.yaml | 8 ++++---- values.yaml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index dcf0ccb..8a43265 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -80,7 +80,7 @@ Server: {{- toYaml .Values.adminUsers | nindent 4 }} {{- end }} {{- if .Values.webPasswordSecret }} - UIPasswordFile: /etc/pelican/web-passwd/password + UIPasswordFile: /etc/pelican/server-web-passwd {{- end }} {{- if .Values.cache.blocksToPrefetch }} diff --git a/templates/deployment.yaml b/templates/deployment.yaml index a6bb913..79ca516 100644 --- a/templates/deployment.yaml +++ b/templates/deployment.yaml @@ -111,8 +111,8 @@ its own CA there. So we use a different directory here and in the Pelican confi {{- if .Values.webPasswordSecret }} - name: web-passwd readOnly: true - mountPath: /etc/pelican/web-passwd/password - subPath: {{ .Values.webPasswordSecretKey | default "password" }} + mountPath: /etc/pelican/server-web-passwd + subPath: {{ .Values.webPasswordSecretKey | default "server-web-passwd" }} {{- end }} {{- if .Values.xrootd.extraConfig }} - name: xrootd-config @@ -245,8 +245,8 @@ its own CA there. So we use a different directory here and in the Pelican confi secret: secretName: {{ .Values.webPasswordSecret }} items: - - key: {{ .Values.webPasswordSecretKey | default "password" }} - path: {{ .Values.webPasswordSecretKey | default "password" }} + - key: {{ .Values.webPasswordSecretKey | default "server-web-passwd" }} + path: {{ .Values.webPasswordSecretKey | default "server-web-passwd" }} mode: 0600 {{- end }} diff --git a/values.yaml b/values.yaml index da2e113..84d8960 100644 --- a/values.yaml +++ b/values.yaml @@ -180,7 +180,7 @@ adminUsers: [] # -- Name of an existing Secret containing the web UI password file webPasswordSecret: "" # -- Key within the web password Secret -webPasswordSecretKey: "password" +webPasswordSecretKey: "server-web-passwd" # -- XRootD configuration xrootd: From bedfd5d1f3282b9984a6328f5f6ebf653023ea68 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Wed, 25 Mar 2026 17:25:09 -0500 Subject: [PATCH 10/37] Rename xrootd.sitename to sitename Since it's a required value, bring it up to the top instead of mixing it in with the rest of the XRootD config. Co-authored-by: Copilot --- ci/houston2-i2-pelican-cache-values.yaml | 4 +++- ci/itb-osdf-pelican-cache-values.yaml | 4 +--- ci/uw-osdf-cache-values.yaml | 2 +- templates/_helpers.tpl | 8 +++++--- values.yaml | 5 +++-- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/ci/houston2-i2-pelican-cache-values.yaml b/ci/houston2-i2-pelican-cache-values.yaml index 0889d04..d1c2451 100644 --- a/ci/houston2-i2-pelican-cache-values.yaml +++ b/ci/houston2-i2-pelican-cache-values.yaml @@ -2,6 +2,7 @@ # from tiger-osg-config. Secrets must be created separately before install. serverHostname: "dtn-pas.hous.nrp.internet2.edu" +sitename: HOUSTON2_INTERNET2_OSDF_CACHE federation: discoveryUrl: "https://osg-htc.org" @@ -83,8 +84,9 @@ adminUsers: "http://cilogon.org/serverE/users/133679 http://cilogon.org/serverA/ webPasswordSecret: osdf-web-pass-nrp webPasswordSecretKey: server-web-passwd +xrootdSitename: HOUSTON2_INTERNET2_OSDF_CACHE + xrootd: - sitename: HOUSTON2_INTERNET2_OSDF_CACHE # The real deployment uses an external ConfigMap (xrootdtrt-cfg) for xrootd config. # xrootd.extraConfig could inline it here if the content were known. extraConfig: "" diff --git a/ci/itb-osdf-pelican-cache-values.yaml b/ci/itb-osdf-pelican-cache-values.yaml index 2f40097..7b2a89c 100644 --- a/ci/itb-osdf-pelican-cache-values.yaml +++ b/ci/itb-osdf-pelican-cache-values.yaml @@ -2,6 +2,7 @@ # Secrets must be created separately before installing the chart. serverHostname: "itb-osdf-pelican-cache.osdf-dev.chtc.io" +sitename: ITB-OSDF-PELICAN-CACHE federation: discoveryUrl: "https://osdf-itb.osg-htc.org" @@ -59,9 +60,6 @@ logging: Ofs: debug Pfc: debug -xrootd: - sitename: ITB-OSDF-PELICAN-CACHE - nodeSelector: datacenter: wid-vlan5 diff --git a/ci/uw-osdf-cache-values.yaml b/ci/uw-osdf-cache-values.yaml index 13f14df..094e0e2 100644 --- a/ci/uw-osdf-cache-values.yaml +++ b/ci/uw-osdf-cache-values.yaml @@ -2,6 +2,7 @@ # Secrets must be created separately before installing the chart. serverHostname: "osdf-uw-cache.svc.osg-htc.org" +sitename: CHTC_PELICAN_CACHE federation: discoveryUrl: "https://osg-htc.org" @@ -95,7 +96,6 @@ webPasswordSecret: osdf-prod-pelican-webserver-passwd webPasswordSecretKey: osdf-pelican-webserver-passwd xrootd: - sitename: CHTC_PELICAN_CACHE extraConfig: | # Set max threads xrd.sched maxt 20000 diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index 8a43265..d08c003 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -131,9 +131,7 @@ Lotman: LotHome: /var/lib/pelican/lotman {{- end }} -{{- if .Values.xrootd.sitename }} -XrootD.Sitename: {{ .Values.xrootd.sitename | quote }} -{{- end }} +XrootD.Sitename: {{ required "sitename is required" .Values.sitename | quote }} {{- if .Values.xrootd.extraConfig }} XrootD.ConfigFile: /etc/pelican/xrootd.conf {{- end }} @@ -246,6 +244,10 @@ Validate required values and conditional requirements. {{- end }} {{- end }} +{{- if eq (trim (default "" .Values.sitename)) "" }} + {{- fail "sitename must be nonempty" }} +{{- end }} + {{- if $loggingPersist }} {{- if not .Values.logging.persistence.existingClaim }} {{- if eq (trim (default "" .Values.logging.persistence.storageClass)) "" }} diff --git a/values.yaml b/values.yaml index 84d8960..27f90fb 100644 --- a/values.yaml +++ b/values.yaml @@ -9,6 +9,9 @@ replicaCount: 1 # -- (Required) External FQDN of the cache server serverHostname: "" +# -- (Required) Site name reported to the federation +sitename: "" + # -- Federation configuration federation: # -- Discovery URL for the federation (OSDF default) @@ -184,8 +187,6 @@ webPasswordSecretKey: "server-web-passwd" # -- XRootD configuration xrootd: - # -- XRootD site name reported to the federation - sitename: "" # -- Raw xrootd.conf content (creates a ConfigMap and mounts it) extraConfig: "" From d62ab22d97a47568ca78ceeb42496795f7a360b8 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Thu, 26 Mar 2026 11:27:02 -0500 Subject: [PATCH 11/37] Change pelican-cache imagePullPolicy to ifNotPresent Do not change the imagePullPolicy for the logrotate image; we want that one to be up to date. Co-authored-by: Copilot --- values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/values.yaml b/values.yaml index 27f90fb..079cf91 100644 --- a/values.yaml +++ b/values.yaml @@ -23,7 +23,7 @@ federation: image: repository: hub.opensciencegrid.org/pelican_platform/osdf-cache tag: "" - pullPolicy: Always + pullPolicy: IfNotPresent # -- Logrotate sidecar configuration logrotate: From 4472c1917aaa43cdff9a448694363331746f5c2d Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Thu, 26 Mar 2026 10:36:52 -0500 Subject: [PATCH 12/37] Add a "sleep" boolean that will cause the cache to sleep instead of running, for debugging Co-authored-by: Copilot --- templates/deployment.yaml | 4 ++++ values.yaml | 3 +++ 2 files changed, 7 insertions(+) diff --git a/templates/deployment.yaml b/templates/deployment.yaml index 79ca516..c38ea88 100644 --- a/templates/deployment.yaml +++ b/templates/deployment.yaml @@ -42,7 +42,11 @@ spec: # ---- Main Pelican cache container ---- - name: pelican-cache image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + {{- if .Values.sleep }} + command: ["sleep", "infinity"] + {{- else }} args: [] + {{- end }} imagePullPolicy: {{ .Values.image.pullPolicy }} {{- with .Values.resources.cache }} resources: diff --git a/values.yaml b/values.yaml index 079cf91..47cd502 100644 --- a/values.yaml +++ b/values.yaml @@ -190,6 +190,9 @@ xrootd: # -- Raw xrootd.conf content (creates a ConfigMap and mounts it) extraConfig: "" +# -- Debugging: set this to true to sleep instead of starting the cache +sleep: false + # -- Network policy networkPolicy: enabled: true From e81cbfec21d682f1e75ec252a799c5e4f31f5552 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Wed, 1 Apr 2026 21:34:08 -0500 Subject: [PATCH 13/37] Update README.md Co-authored-by: Copilot --- README.md | 262 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 186 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index 09cd1f0..9e309ea 100644 --- a/README.md +++ b/README.md @@ -16,18 +16,33 @@ This Helm chart collapses these three layers into a single, parameterized chart - Kubernetes 1.24+ - Helm 3.x -- [cert-manager](https://cert-manager.io/) (if using `certificate.enabled: true`) -- Pre-created Secrets for sensitive data (issuer keys, OIDC credentials, web UI passwords) +- Optional: [cert-manager](https://cert-manager.io/) for creating certificates. +- Pre-created Secrets for sensitive data (issuer keys, OIDC credentials, web UI passwords), + see "Create Required Secrets" below. + ## Quick Start +### Create Required Secrets + +The following secrets must exist before the cache can be started: + +- Web server admin key + Create this with `pelican generate password` + +- Issuer key + Create this with `pelican key create` + ### Minimal installation ```bash helm install my-cache ./pelican-cache \ --set serverHostname=my-cache.example.com \ - --set cache.storageClassName=my-storage-class \ - --set logging.storageClassName=my-storage-class + --set sitename=my-site \ + --set cache.pvc.storageClass=my-storage-class \ + --set logging.persistence.storageClass=my-storage-class \ + --set issuerKey.pvc.storageClass=my-storage-class \ + --set webPasswordSecret=my-web-passwd-secret ``` ### Installation with a values file @@ -62,47 +77,123 @@ The chart manages several persistent volumes: | Volume | Purpose | Backing | |---|---|---| -| Cache data | XRootD file cache | PVC or hostPath (`cache.storageType`) | +| Cache data | XRootD file cache | PVC or hostPath (`cache.type`) | | Logging | Pelican log files | PVC (always) | -| Namespace key | Pelican issuer/signing key | PVC or existing Secret (`namespaceKey.type`) | +| Issuer key | Pelican issuer/signing key | PVC or existing Secret (`issuerKey.type`) | | Lotman data | Lot-based storage management | PVC (when `lotman.enabled`) | **NVMe storage is strongly recommended for the cache data volume.** ## Configuration Reference -### Required Values +### Site Identity (customization required) | Parameter | Description | |---|---| | `serverHostname` | External FQDN of the cache. Chart fails to render without this. | +| `sitename` | Site name reported to the federation | + +Both of these values are required for the cache to be able to identify itself to the federation. + +### Cache Storage (customization required) + +Cache persistence must be specified; you must choose a value for `cache.type`, either `pvc` or `hostPath`, +and then fill out the fields in the appropriate subsections. -### Images +**PVC option** (`cache.type: pvc`): Use Kubernetes persistent volumes, creating new or referencing existing PVCs. + +If using a PVC, you can use an existing PVC or have the chart create a new one. +If using an existing PVC, you must set `cache.pvc.existingClaim` to the name of the PVC. +If creating a new PVC, you must set `cache.pvc.storageClass` to one of the available storage classes in your cluster. +PVCs created by this chart will not be deleted on uninstall. + +**HostPath option** (`cache.type: hostPath`): Bind-mount a directory from the node. + +If using a host path, you must specify the path in `cache.hostPath.path`. | Parameter | Default | Description | |---|---|---| -| `image.repository` | `hub.opensciencegrid.org/pelican_platform/osdf-cache` | Cache container image | -| `image.tag` | `v7.23.0` | Cache image tag | -| `image.pullPolicy` | `Always` | Image pull policy | -| `logrotateImage.repository` | `hub.opensciencegrid.org/opensciencegrid/logrotate` | Logrotate sidecar image | -| `logrotateImage.tag` | `24-release` | Logrotate image tag | +| `cache.type` | `pvc` | `pvc` or `hostPath` | +| `cache.hostPath.path` | `""` | Host path (required when type is `hostPath`) | +| `cache.pvc.existingClaim` | `""` | Existing PVC name (if set, ignores `storageClass` and `size`) | +| `cache.pvc.storageClass` | `""` | StorageClass for new cache data PVC (required if `existingClaim` is not set) | +| `cache.pvc.size` | `1000Gi` | Cache data PVC size (used when creating a new PVC) | + +### Issuer Key (customization required) + +| Parameter | Default | Description | +|---|---|---| +| `issuerKey.type` | `pvc` | `pvc` (Pelican auto-generates) or `existingSecret` | +| `issuerKey.pvc.storageClass` | `""` | StorageClass for key PVC | +| `issuerKey.pvc.size` | `10Mi` | Key PVC size | +| `issuerKey.existingSecret` | `""` | Pre-existing Secret name (when type is `existingSecret`) | +| `issuerKey.secretKey` | `private-key.pem` | Key within the Secret | -### Federation +You must specify a way to store the issuer key (which is the key Pelican uses to sign credentials +and authenticate itself to the federation). +Your options are: + +1. Have Pelican generate the key and save it to a PVC that the chart creates. + To do this, specify `issuerKey.type: pvc` and `issuerKey.pvc.storageClass` to one of the available storage types in your cluster. + PVCs created by this chart will not be deleted on uninstall. + +2. Pre-create the key using the `pelican key create` command, and save it as a secret. + To do this, specify `issuerKey.type: existingSecret` and specify the secret name as `issuerKey.existingSecret`. + +### Logging (customization required) + +| Parameter | Default | Description | +|---|---|---| +| `logging.persistence.separateVolume` | `true` | Provision a dedicated PVC for `/var/log/pelican`; when `false`, the data volume is also mounted at `/var/log` so `/var/log/pelican` can still live on cache storage. Logrotate always runs. | +| `logging.level` | `INFO` | Global Pelican log level | +| `logging.persistence.existingClaim` | `""` | Existing logging PVC name (if set, ignores `storageClass` and `size`) | +| `logging.persistence.storageClass` | `""` | StorageClass for new logging PVC (required if `existingClaim` is not set and `separateVolume=true`) | +| `logging.persistence.size` | `50Gi` | Logging PVC size (used when creating a new PVC) | +| `logging.cache` | `{}` | Per-subsystem log levels (map, e.g. `{Pss: debug, Pfc: debug}`) | + +### Admin / Web UI (customization required) + +| Parameter | Default | Description | +|---|---|---| +| `adminUsers` | `[]` | List of CILogon admin user identities | +| `webPasswordSecret` | `""` | Existing Secret for web UI password | +| `webPasswordSecretKey` | `server-web-passwd` | Key within the web password Secret | + +You must create a secret containing a file named `server-web-passwd` that was created by running `pelican generate password` +and specify that as `webPasswordSecret`. + +### Federation (customization optional) | Parameter | Default | Description | |---|---|---| | `federation.discoveryUrl` | `https://osg-htc.org` | Federation discovery URL. Change for non-OSDF or ITB. | +| `federation.label` | `osdf` | Resource label indicating the federation. This must match the discovery URL when set to known values. | + +The federation label and discovery URL have to match for the OSDF and OSDF-ITB federations. +The valid pairs are: -### Cache Storage +| discoveryUrl | label | +|---|---| +| https://osg-htc.org | osdf | +| https://osdf-itb.osg-htc.org | osdf-itb | + +Checks are not performed for other federations. +The default federation is OSDF so OSDF caches do not need to change this section. + +### Images (customization optional) | Parameter | Default | Description | |---|---|---| -| `cache.storageType` | `pvc` | `pvc` or `hostPath` | -| `cache.hostPath` | `""` | Host path (required when storageType is `hostPath`) | -| `cache.storageClassName` | `""` | StorageClass for cache data PVC | -| `cache.pvcSize` | `1000Gi` | Cache data PVC size | +| `image.repository` | `hub.opensciencegrid.org/pelican_platform/osdf-cache` | Cache container image | +| `image.tag` | `""` | Cache image tag (defaults to chart `appVersion` when empty) | +| `image.pullPolicy` | `IfNotPresent` | Image pull policy for the cache container | +| `logrotate.image.repository` | `hub.opensciencegrid.org/opensciencegrid/logrotate` | Logrotate sidecar image | +| `logrotate.image.tag` | `24-release` | Logrotate image tag | -### Cache Tuning +The logrotate sidecar always uses `imagePullPolicy: Always` and that behavior is +not configurable. + +### Cache Tuning (customization optional) | Parameter | Default | Description | |---|---|---| @@ -116,16 +207,6 @@ The chart manages several persistent volumes: For details on `Files*Size` parameters, see the [XRootD PFC documentation](https://xrootd.web.cern.ch/doc/dev56/pss_config.pdf) (search for "diskusage"). -### Issuer / Namespace Key - -| Parameter | Default | Description | -|---|---|---| -| `namespaceKey.type` | `pvc` | `pvc` (Pelican auto-generates) or `existingSecret` | -| `namespaceKey.pvc.storageClassName` | `""` | StorageClass for key PVC | -| `namespaceKey.pvc.size` | `10Mi` | Key PVC size | -| `namespaceKey.existingSecret` | `""` | Pre-existing Secret name (when type is `existingSecret`) | -| `namespaceKey.secretKey` | `issuer.pem` | Key within the Secret | - ### TLS / Certificates `serverHostname` is always included in the rendered cert-manager `Certificate.spec.dnsNames`; use `certificate.dnsNames` only for additional SANs. @@ -138,7 +219,7 @@ For details on `Files*Size` parameters, see the [XRootD PFC documentation](https | `certificate.dnsNames` | `[]` | Additional DNS SANs (`serverHostname` is always included) | | `tls.existingSecret` | `""` | Use an existing TLS Secret instead of cert-manager | -### Service +### Service (customization optional) | Parameter | Default | Description | |---|---|---| @@ -147,60 +228,49 @@ For details on `Files*Size` parameters, see the [XRootD PFC documentation](https | `service.externalTrafficPolicy` | `Local` | Traffic policy | | `service.annotations` | `{}` | Extra annotations (external-dns, metallb, etc.) | -### Resources +### Resources (customization optional) | Parameter | Default | Description | |---|---|---| | `resources.cache.requests.cpu` | `1000m` | Cache container CPU request | | `resources.cache.requests.memory` | `16Gi` | Cache container memory request | | `resources.cache.limits` | `{}` | Cache container limits | -| `resources.logrotate.requests.cpu` | `1` | Logrotate CPU request | -| `resources.logrotate.requests.memory` | `500M` | Logrotate memory request | -| `resources.logrotate.limits.cpu` | `2` | Logrotate CPU limit | -| `resources.logrotate.limits.memory` | `2G` | Logrotate memory limit | - -### Logging - -| Parameter | Default | Description | -|---|---|---| -| `logging.level` | `INFO` | Global Pelican log level | -| `logging.storageClassName` | `""` | StorageClass for logging PVC | -| `logging.pvcSize` | `5Gi` | Logging PVC size | -| `logging.cache` | `{}` | Per-subsystem log levels (map, e.g. `{Pss: debug, Pfc: debug}`) | +| `logrotate.resources.requests.cpu` | `1` | Logrotate CPU request | +| `logrotate.resources.requests.memory` | `500M` | Logrotate memory request | +| `logrotate.resources.limits.cpu` | `2` | Logrotate CPU limit | +| `logrotate.resources.limits.memory` | `2G` | Logrotate memory limit | ### Optional Components | Parameter | Default | Description | |---|---|---| +| `sleep` | `false` | Debug mode: run `sleep infinity` in the `pelican-cache` container instead of starting the cache process | | `cvmfsRedirector.enabled` | `false` | Enable CVMFS port redirector sidecar | | `lotman.enabled` | `false` | Enable Lotman (lot-based storage management) | -| `lotman.storageClassName` | `""` | StorageClass for Lotman PVC | -| `lotman.pvcSize` | `10Gi` | Lotman PVC size | +| `lotman.pvc.existingClaim` | `""` | Existing Lotman PVC name (if set, ignores `storageClass` and `size`) | +| `lotman.pvc.storageClass` | `""` | StorageClass for new Lotman PVC (required if `existingClaim` is not set and `enabled=true`) | +| `lotman.pvc.size` | `10Gi` | Lotman PVC size (used when creating a new PVC) | | `oidc.enabled` | `false` | Enable OIDC authentication | | `oidc.existingSecret` | `""` | Secret with `client.id` and `client.secret` keys | -### Admin / Web UI - -| Parameter | Default | Description | -|---|---|---| -| `adminUsers` | `""` | Space-separated CILogon admin user identities | -| `webPasswordSecret` | `""` | Existing Secret for web UI password | -| `webPasswordSecretKey` | `password` | Key within the web password Secret | +When `sleep` is `true`, the `pelican-cache` container starts with `sleep infinity` for debugging. After you `kubectl exec` into the container, you can start the cache manually with `pelican cache serve`. -### XRootD +### XRootD (customization optional) | Parameter | Default | Description | |---|---|---| -| `xrootd.sitename` | `""` | XRootD site name reported to the federation | | `xrootd.extraConfig` | `""` | Raw `xrootd.conf` content (e.g. `xrd.sched maxt 20000`) | +`xrootd.extraConfig` is an escape hatch for settings that cannot be expressed +through normal chart values. Prefer regular chart parameters when possible. + ### Network Policy | Parameter | Default | Description | |---|---|---| | `networkPolicy.enabled` | `true` | Create a NetworkPolicy | -### Scheduling +### Scheduling (customization optional) | Parameter | Default | Description | |---|---|---| @@ -208,7 +278,7 @@ For details on `Files*Size` parameters, see the [XRootD PFC documentation](https | `tolerations` | `[]` | Pod tolerations | | `affinity` | `{}` | Pod affinity rules | -### Escape Hatches +### Escape Hatches (customization optional) | Parameter | Default | Description | |---|---|---| @@ -225,36 +295,40 @@ For details on `Files*Size` parameters, see the [XRootD PFC documentation](https ```yaml serverHostname: my-cache.osg-htc.org +sitename: MY_OSDF_CACHE cache: - storageClassName: fast-nvme - pvcSize: 2000Gi + type: pvc + pvc: + storageClass: fast-nvme + size: 2000Gi logging: - storageClassName: standard + persistence: + storageClass: standard -certificate: - dnsNames: - - my-cache-alt.example.org +webPasswordSecret: my-web-passwd-secret ``` ### Production Cache (hostPath, like uw-osdf-cache) ```yaml serverHostname: osdf-uw-cache.svc.osg-htc.org +sitename: MY_PELICAN_CACHE image: tag: "v7.23.0" cache: - storageType: hostPath - hostPath: /srv/pelican-cache/ + type: hostPath + hostPath: + path: /srv/pelican-cache/ blocksToPrefetch: 10 concurrency: 240 highWaterMark: 27000g lowWaterMark: 25000g -namespaceKey: +issuerKey: type: existingSecret existingSecret: my-cache-issuer-key @@ -278,25 +352,28 @@ resources: logging: level: debug - storageClassName: 3x-replica-hdd - pvcSize: 50Gi + persistence: + storageClass: 3x-replica-hdd + size: 50Gi cache: Pss: debug Pfc: debug lotman: enabled: true - storageClassName: 3x-replica-hdd + pvc: + storageClass: 3x-replica-hdd oidc: enabled: true existingSecret: osdf-component-oidc -adminUsers: "http://cilogon.org/serverE/users/12345 http://cilogon.org/serverA/users/67890" +adminUsers: + - "http://cilogon.org/serverE/users/12345" + - "http://cilogon.org/serverA/users/67890" webPasswordSecret: my-web-passwd-secret xrootd: - sitename: MY_PELICAN_CACHE extraConfig: | xrd.sched maxt 20000 @@ -325,9 +402,42 @@ image: repository: hub.opensciencegrid.org/pelican_platform/cache cache: - storageClassName: local-nvme + type: pvc + pvc: + storageClass: local-nvme ``` +## Validation Requirements + +The chart enforces the following validation rules at render time to ensure a valid configuration: + +**Storage:** +- If `cache.type` is `pvc`: + - If `cache.pvc.existingClaim` is empty, `cache.pvc.storageClass` must be nonempty + - If `cache.pvc.existingClaim` is set, `storageClass` and `size` are ignored +- If `cache.type` is `hostPath`: + - `cache.hostPath.path` must be nonempty + +**Issuer Key:** +- If `issuerKey.type` is `pvc`, `issuerKey.pvc.storageClass` must be nonempty +- If `issuerKey.type` is `existingSecret`, `issuerKey.existingSecret` must be nonempty + +**Logging:** +- If `logging.persistence.separateVolume` is `true` and `logging.persistence.existingClaim` is empty, `logging.persistence.storageClass` must be nonempty + +**Lotman:** +- If `lotman.enabled` is `true` and `lotman.pvc.existingClaim` is empty, `lotman.pvc.storageClass` must be nonempty + +**OIDC:** +- If `oidc.enabled` is `true`, `oidc.existingSecret` must be nonempty + +**Web UI:** +- `webPasswordSecret` must be nonempty + +**Federation:** +- `serverHostname` must be nonempty +- If `federation.discoveryUrl` or `federation.label` match a known federation pair (OSDF or OSDF-ITB), both must be set consistently + ## Secrets Management This chart **does not create Secrets**. All sensitive material must be provisioned separately before installing the chart. Common approaches: @@ -340,9 +450,9 @@ Secrets the chart may reference: | Value pointing to Secret | Keys expected | Purpose | |---|---|---| -| `namespaceKey.existingSecret` | Key named per `namespaceKey.secretKey` (default: `issuer.pem`) | Pelican issuer/signing key | +| `issuerKey.existingSecret` | Key named per `issuerKey.secretKey` (default: `private-key.pem`) | Pelican issuer/signing key | | `oidc.existingSecret` | `client.id`, `client.secret` | OIDC client credentials | -| `webPasswordSecret` | Key named per `webPasswordSecretKey` (default: `password`) | Web UI password file | +| `webPasswordSecret` | Key named per `webPasswordSecretKey` (default: `server-web-passwd`) | Web UI password file | | `tls.existingSecret` | `tls.crt`, `tls.key` | TLS certificate (if not using cert-manager) | ## Upgrading @@ -377,7 +487,7 @@ This chart is a standalone replacement for the Kustomize + Flux deployment model | Instance `deployment-patch.yaml` | Values: `resources`, `nodeSelector`, `securityContext`, `extraEnv`, etc. | | Instance `service-patch.yaml` | Values: `service.*` | | Instance `cert-patch.yaml` | Values: `certificate.dnsNames` | -| Instance PVC patches | Values: `cache.storageType`, `logging.*`, `namespaceKey.*`, `lotman.*` | +| Instance PVC patches | Values: `cache.type`, `cache.hostPath.path`, `cache.pvc.*`, `logging.*`, `issuerKey.*`, `lotman.*` | ## License From 40e36962b65596034380a88be6bafa90ad213304 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Tue, 31 Mar 2026 15:42:11 -0500 Subject: [PATCH 14/37] Move `certManager` configuration under `tls` Nest the certManager config under tls so certificate-related knobs are specified together. Co-authored-by: Copilot --- README.md | 23 ++++++++++++----------- ci/houston2-i2-pelican-cache-values.yaml | 5 ++--- ci/itb-osdf-pelican-cache-values.yaml | 15 ++++++++------- ci/uw-osdf-cache-values.yaml | 17 +++++++++-------- templates/NOTES.txt | 6 +++--- templates/_helpers.tpl | 8 ++++++++ templates/certificate.yaml | 8 ++++---- values.yaml | 24 +++++++++++------------- 8 files changed, 57 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 9e309ea..017fced 100644 --- a/README.md +++ b/README.md @@ -209,14 +209,14 @@ For details on `Files*Size` parameters, see the [XRootD PFC documentation](https ### TLS / Certificates -`serverHostname` is always included in the rendered cert-manager `Certificate.spec.dnsNames`; use `certificate.dnsNames` only for additional SANs. +`serverHostname` is always included in the rendered cert-manager `Certificate.spec.dnsNames`; use `tls.certManager.dnsNames` only for additional SANs. | Parameter | Default | Description | |---|---|---| -| `certificate.enabled` | `true` | Create a cert-manager Certificate | -| `certificate.issuerRef.name` | `letsencrypt-prod` | Issuer name | -| `certificate.issuerRef.kind` | `ClusterIssuer` | Issuer kind | -| `certificate.dnsNames` | `[]` | Additional DNS SANs (`serverHostname` is always included) | +| `tls.certManager.enabled` | `true` | Create a cert-manager Certificate | +| `tls.certManager.issuerRef.name` | `letsencrypt-prod` | Issuer name | +| `tls.certManager.issuerRef.kind` | `ClusterIssuer` | Issuer kind | +| `tls.certManager.dnsNames` | `[]` | Additional DNS SANs (`serverHostname` is always included) | | `tls.existingSecret` | `""` | Use an existing TLS Secret instead of cert-manager | ### Service (customization optional) @@ -332,11 +332,12 @@ issuerKey: type: existingSecret existingSecret: my-cache-issuer-key -certificate: - enabled: true - dnsNames: - - osdf-uw-cache.svc.osdf-prod.chtc.io - - osdf-uw-cache.svc.osg-htc.org +tls: + certManager: + enabled: true + dnsNames: + - osdf-uw-cache.svc.osdf-prod.chtc.io + - osdf-uw-cache.svc.osg-htc.org service: loadBalancerIP: "128.105.82.176" @@ -486,7 +487,7 @@ This chart is a standalone replacement for the Kustomize + Flux deployment model | Instance `50-instance.yaml` | Generated by `_helpers.tpl` and stored in `configmap-pelican.yaml` | | Instance `deployment-patch.yaml` | Values: `resources`, `nodeSelector`, `securityContext`, `extraEnv`, etc. | | Instance `service-patch.yaml` | Values: `service.*` | -| Instance `cert-patch.yaml` | Values: `certificate.dnsNames` | +| Instance `cert-patch.yaml` | Values: `tls.certManager.dnsNames` | | Instance PVC patches | Values: `cache.type`, `cache.hostPath.path`, `cache.pvc.*`, `logging.*`, `issuerKey.*`, `lotman.*` | ## License diff --git a/ci/houston2-i2-pelican-cache-values.yaml b/ci/houston2-i2-pelican-cache-values.yaml index d1c2451..643c56d 100644 --- a/ci/houston2-i2-pelican-cache-values.yaml +++ b/ci/houston2-i2-pelican-cache-values.yaml @@ -31,10 +31,9 @@ issuerKey: # The real deployment creates its own Certificate (Issuer, not ClusterIssuer) # and uses a differently named secret ("certs"). # We use tls.existingSecret to reference that pre-existing secret. -certificate: - enabled: false - tls: + certManager: + enabled: false existingSecret: houston2-i2-certs # hostNetwork: true binds directly to the node's network and disables the Service. diff --git a/ci/itb-osdf-pelican-cache-values.yaml b/ci/itb-osdf-pelican-cache-values.yaml index 7b2a89c..21cbbf4 100644 --- a/ci/itb-osdf-pelican-cache-values.yaml +++ b/ci/itb-osdf-pelican-cache-values.yaml @@ -27,13 +27,14 @@ issuerKey: existingSecret: itb-osdf-pelican-cache-issuer-keys secretKey: issuer.pem -certificate: - enabled: true - issuerRef: - name: letsencrypt-prod - kind: ClusterIssuer - dnsNames: - - itb-osdf-pelican-cache.osdf-dev.chtc.io +tls: + certManager: + enabled: true + issuerRef: + name: letsencrypt-prod + kind: ClusterIssuer + dnsNames: + - itb-osdf-pelican-cache.osdf-dev.chtc.io service: type: LoadBalancer diff --git a/ci/uw-osdf-cache-values.yaml b/ci/uw-osdf-cache-values.yaml index 094e0e2..9fc8ccf 100644 --- a/ci/uw-osdf-cache-values.yaml +++ b/ci/uw-osdf-cache-values.yaml @@ -32,14 +32,15 @@ issuerKey: existingSecret: uw-osdf-cache-issuer-key secretKey: issuer.pem -certificate: - enabled: true - issuerRef: - name: letsencrypt-prod - kind: ClusterIssuer - dnsNames: - - osdf-uw-cache.svc.osdf-prod.chtc.io - - osdf-uw-cache.svc.osg-htc.org +tls: + certManager: + enabled: true + issuerRef: + name: letsencrypt-prod + kind: ClusterIssuer + dnsNames: + - osdf-uw-cache.svc.osdf-prod.chtc.io + - osdf-uw-cache.svc.osg-htc.org service: type: LoadBalancer diff --git a/templates/NOTES.txt b/templates/NOTES.txt index 77a1fb5..233d91c 100644 --- a/templates/NOTES.txt +++ b/templates/NOTES.txt @@ -5,10 +5,10 @@ Pelican Cache {{ .Chart.AppVersion }} has been deployed. Hostname: {{ .Values.serverHostname }} Federation: {{ .Values.federation.discoveryUrl }} -{{- if .Values.certificate.enabled }} +{{- if .Values.tls.certManager.enabled }} -TLS certificate will be issued by {{ .Values.certificate.issuerRef.kind }}/{{ .Values.certificate.issuerRef.name }}. -DNS names: {{ .Values.serverHostname }}{{- range .Values.certificate.dnsNames }}, {{ . }}{{- end }} +TLS certificate will be issued by {{ .Values.tls.certManager.issuerRef.kind }}/{{ .Values.tls.certManager.issuerRef.name }}. +DNS names: {{ .Values.serverHostname }}{{- range .Values.tls.certManager.dnsNames }}, {{ . }}{{- end }} {{- end }} Check deployment status: diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index d08c003..85aff00 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -273,5 +273,13 @@ Validate required values and conditional requirements. {{- if eq (trim (default "" .Values.webPasswordSecret)) "" }} {{- fail "webPasswordSecret must be nonempty" }} {{- end }} + +{{- if and .Values.tls.certManager.enabled .Values.tls.existingSecret }} + {{- fail "tls.existingSecret and tls.certManager.enabled cannot both be set; choose exactly one TLS source" }} +{{- end }} + +{{- if and (not .Values.tls.certManager.enabled) (eq (trim (default "" .Values.tls.existingSecret)) "") }} + {{- fail "tls.existingSecret must be nonempty when tls.certManager.enabled is false" }} +{{- end }} {{- end }} diff --git a/templates/certificate.yaml b/templates/certificate.yaml index 25e9c1c..38742ab 100644 --- a/templates/certificate.yaml +++ b/templates/certificate.yaml @@ -1,5 +1,5 @@ -{{- if .Values.certificate.enabled }} -{{- $dnsNames := concat (list (required "serverHostname is required" .Values.serverHostname)) (.Values.certificate.dnsNames | default (list)) | uniq }} +{{- if .Values.tls.certManager.enabled }} +{{- $dnsNames := concat (list (required "serverHostname is required" .Values.serverHostname)) (.Values.tls.certManager.dnsNames | default (list)) | uniq }} apiVersion: cert-manager.io/v1 kind: Certificate metadata: @@ -10,7 +10,7 @@ spec: dnsNames: {{- toYaml $dnsNames | nindent 4 }} issuerRef: - name: {{ .Values.certificate.issuerRef.name }} - kind: {{ .Values.certificate.issuerRef.kind }} + name: {{ .Values.tls.certManager.issuerRef.name }} + kind: {{ .Values.tls.certManager.issuerRef.kind }} secretName: {{ include "pelican-cache.fullname" . }}-tls {{- end }} diff --git a/values.yaml b/values.yaml index 47cd502..1f4a458 100644 --- a/values.yaml +++ b/values.yaml @@ -99,20 +99,18 @@ issuerKey: # -- Key within the Secret that contains the issuer key file secretKey: "private-key.pem" -# -- TLS certificate configuration (via cert-manager) -certificate: - # -- Create a cert-manager Certificate resource - enabled: true - issuerRef: - # -- Name of the ClusterIssuer or Issuer - name: letsencrypt-prod - # -- Kind: ClusterIssuer or Issuer - kind: ClusterIssuer - # -- Additional DNS Subject Alternative Names for the certificate (serverHostname is always included) - dnsNames: [] - -# -- Use an existing TLS Secret instead of cert-manager tls: + certManager: + # -- Create a cert-manager Certificate resource + enabled: true + issuerRef: + # -- Name of the ClusterIssuer or Issuer + name: letsencrypt-prod + # -- Kind: ClusterIssuer or Issuer + kind: ClusterIssuer + # -- Additional DNS Subject Alternative Names for the certificate (serverHostname is always included) + dnsNames: [] + # -- Use an existing TLS Secret instead of cert-manager existingSecret: "" # -- Service configuration From 22d486027db1919cc4532c3793bb1089de054d07 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Tue, 31 Mar 2026 15:52:00 -0500 Subject: [PATCH 15/37] Move cache resources under cache block Put the resource requests/limits for the cache container itself under the cache block, for consistency with the way we set resources/limits for the logrotate container. Co-authored-by: Copilot --- README.md | 16 +++++++--------- ci/houston2-i2-pelican-cache-values.yaml | 16 +++++++--------- ci/itb-osdf-pelican-cache-values.yaml | 10 ++++------ ci/uw-osdf-cache-values.yaml | 10 ++++------ templates/deployment.yaml | 2 +- values.yaml | 13 +++++-------- 6 files changed, 28 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 017fced..bde3a49 100644 --- a/README.md +++ b/README.md @@ -232,9 +232,9 @@ For details on `Files*Size` parameters, see the [XRootD PFC documentation](https | Parameter | Default | Description | |---|---|---| -| `resources.cache.requests.cpu` | `1000m` | Cache container CPU request | -| `resources.cache.requests.memory` | `16Gi` | Cache container memory request | -| `resources.cache.limits` | `{}` | Cache container limits | +| `cache.resources.requests.cpu` | `1000m` | Cache container CPU request | +| `cache.resources.requests.memory` | `16Gi` | Cache container memory request | +| `cache.resources.limits` | `{}` | Cache container limits | | `logrotate.resources.requests.cpu` | `1` | Logrotate CPU request | | `logrotate.resources.requests.memory` | `500M` | Logrotate memory request | | `logrotate.resources.limits.cpu` | `2` | Logrotate CPU limit | @@ -327,6 +327,10 @@ cache: concurrency: 240 highWaterMark: 27000g lowWaterMark: 25000g + resources: + requests: + cpu: "24" + memory: "48Gi" issuerKey: type: existingSecret @@ -345,12 +349,6 @@ service: external-dns.alpha.kubernetes.io/hostname: osdf-uw-cache.svc.osdf-prod.chtc.io metallb.universe.tf/address-pool: tiger-vlan5 -resources: - cache: - requests: - cpu: "24" - memory: "48Gi" - logging: level: debug persistence: diff --git a/ci/houston2-i2-pelican-cache-values.yaml b/ci/houston2-i2-pelican-cache-values.yaml index 643c56d..19e4886 100644 --- a/ci/houston2-i2-pelican-cache-values.yaml +++ b/ci/houston2-i2-pelican-cache-values.yaml @@ -22,6 +22,13 @@ cache: hostPath: path: /cache concurrency: 160 + resources: + requests: + cpu: "16000m" + memory: "210Gi" + limits: + cpu: "16000m" + memory: "210Gi" issuerKey: type: existingSecret @@ -42,15 +49,6 @@ hostNetwork: true networkPolicy: enabled: false -resources: - cache: - requests: - cpu: "16000m" - memory: "210Gi" - limits: - cpu: "16000m" - memory: "210Gi" - logrotate: resources: requests: diff --git a/ci/itb-osdf-pelican-cache-values.yaml b/ci/itb-osdf-pelican-cache-values.yaml index 21cbbf4..2919b00 100644 --- a/ci/itb-osdf-pelican-cache-values.yaml +++ b/ci/itb-osdf-pelican-cache-values.yaml @@ -21,6 +21,10 @@ cache: storageClass: 3x-replica-hdd-raddus size: 100Gi concurrency: 40 + resources: + requests: + cpu: "8" + memory: "16Gi" issuerKey: type: existingSecret @@ -42,12 +46,6 @@ service: external-dns.alpha.kubernetes.io/hostname: itb-osdf-pelican-cache.osdf-dev.chtc.io metallb.universe.tf/address-pool: tiger-vlan5 -resources: - cache: - requests: - cpu: "8" - memory: "16Gi" - logging: level: "debug" persistence: diff --git a/ci/uw-osdf-cache-values.yaml b/ci/uw-osdf-cache-values.yaml index 9fc8ccf..4d2b2ef 100644 --- a/ci/uw-osdf-cache-values.yaml +++ b/ci/uw-osdf-cache-values.yaml @@ -26,6 +26,10 @@ cache: filesMaxSize: 17500g filesNominalSize: 17000g filesBaseSize: 16500g + resources: + requests: + cpu: "24" + memory: "48Gi" issuerKey: type: existingSecret @@ -49,12 +53,6 @@ service: external-dns.alpha.kubernetes.io/hostname: osdf-uw-cache.svc.osdf-prod.chtc.io metallb.universe.tf/address-pool: tiger-vlan5 -resources: - cache: - requests: - cpu: "24" - memory: "48Gi" - logrotate: resources: requests: diff --git a/templates/deployment.yaml b/templates/deployment.yaml index c38ea88..e154275 100644 --- a/templates/deployment.yaml +++ b/templates/deployment.yaml @@ -48,7 +48,7 @@ spec: args: [] {{- end }} imagePullPolicy: {{ .Values.image.pullPolicy }} - {{- with .Values.resources.cache }} + {{- with .Values.cache.resources }} resources: {{- toYaml . | nindent 12 }} {{- end }} diff --git a/values.yaml b/values.yaml index 1f4a458..ccb5742 100644 --- a/values.yaml +++ b/values.yaml @@ -84,6 +84,11 @@ cache: filesNominalSize: "" # -- diskusage base file tracking size (e.g. "16500g") filesBaseSize: "" + resources: + requests: + cpu: "1000m" + memory: "16Gi" + limits: {} # -- Issuer key configuration issuerKey: @@ -122,14 +127,6 @@ service: # -- Extra annotations (e.g. external-dns, metallb) annotations: {} -# -- Container resource requests and limits -resources: - cache: - requests: - cpu: "1000m" - memory: "16Gi" - limits: {} - # -- Logging configuration logging: persistence: From d56a2bea229701585376be4970f9b5f4bf60fc5c Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Tue, 31 Mar 2026 15:54:25 -0500 Subject: [PATCH 16/37] Nest the web password secret configuration Rename webPasswordSecret and webPasswordSecretKey to webPassword.existingSecret and webPassword.key, for consistency with the issuerKey and tls config. Co-authored-by: Copilot --- README.md | 18 ++++++++++-------- ci/houston2-i2-pelican-cache-values.yaml | 5 +++-- ci/itb-osdf-pelican-cache-values.yaml | 5 +++-- ci/uw-osdf-cache-values.yaml | 5 +++-- templates/_helpers.tpl | 6 +++--- templates/deployment.yaml | 12 ++++++------ values.yaml | 9 +++++---- 7 files changed, 33 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index bde3a49..00ebfc9 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ helm install my-cache ./pelican-cache \ --set cache.pvc.storageClass=my-storage-class \ --set logging.persistence.storageClass=my-storage-class \ --set issuerKey.pvc.storageClass=my-storage-class \ - --set webPasswordSecret=my-web-passwd-secret + --set webPassword.existingSecret=my-web-passwd-secret ``` ### Installation with a values file @@ -156,11 +156,11 @@ Your options are: | Parameter | Default | Description | |---|---|---| | `adminUsers` | `[]` | List of CILogon admin user identities | -| `webPasswordSecret` | `""` | Existing Secret for web UI password | -| `webPasswordSecretKey` | `server-web-passwd` | Key within the web password Secret | +| `webPassword.existingSecret` | `""` | Existing Secret for web UI password | +| `webPassword.key` | `server-web-passwd` | Key within the web password Secret | You must create a secret containing a file named `server-web-passwd` that was created by running `pelican generate password` -and specify that as `webPasswordSecret`. +and specify that as `webPassword.existingSecret`. ### Federation (customization optional) @@ -307,7 +307,8 @@ logging: persistence: storageClass: standard -webPasswordSecret: my-web-passwd-secret +webPassword: + existingSecret: my-web-passwd-secret ``` ### Production Cache (hostPath, like uw-osdf-cache) @@ -370,7 +371,8 @@ oidc: adminUsers: - "http://cilogon.org/serverE/users/12345" - "http://cilogon.org/serverA/users/67890" -webPasswordSecret: my-web-passwd-secret +webPassword: + existingSecret: my-web-passwd-secret xrootd: extraConfig: | @@ -431,7 +433,7 @@ The chart enforces the following validation rules at render time to ensure a val - If `oidc.enabled` is `true`, `oidc.existingSecret` must be nonempty **Web UI:** -- `webPasswordSecret` must be nonempty +- `webPassword.existingSecret` must be nonempty **Federation:** - `serverHostname` must be nonempty @@ -451,7 +453,7 @@ Secrets the chart may reference: |---|---|---| | `issuerKey.existingSecret` | Key named per `issuerKey.secretKey` (default: `private-key.pem`) | Pelican issuer/signing key | | `oidc.existingSecret` | `client.id`, `client.secret` | OIDC client credentials | -| `webPasswordSecret` | Key named per `webPasswordSecretKey` (default: `server-web-passwd`) | Web UI password file | +| `webPassword.existingSecret` | Key named per `webPassword.key` (default: `server-web-passwd`) | Web UI password file | | `tls.existingSecret` | `tls.crt`, `tls.key` | TLS certificate (if not using cert-manager) | ## Upgrading diff --git a/ci/houston2-i2-pelican-cache-values.yaml b/ci/houston2-i2-pelican-cache-values.yaml index 19e4886..732803e 100644 --- a/ci/houston2-i2-pelican-cache-values.yaml +++ b/ci/houston2-i2-pelican-cache-values.yaml @@ -78,8 +78,9 @@ oidc: adminUsers: "http://cilogon.org/serverE/users/133679 http://cilogon.org/serverA/users/10832 http://cilogon.org/serverA/users/21441 http://cilogon.org/serverA/users/46022246 http://cilogon.org/serverA/users/9265706 http://cilogon.org/serverE/users/57152 http://cilogon.org/serverE/users/130835 http://cilogon.org/serverE/users/245993 http://cilogon.org/serverB/users/51444962" -webPasswordSecret: osdf-web-pass-nrp -webPasswordSecretKey: server-web-passwd +webPassword: + existingSecret: osdf-web-pass-nrp + key: server-web-passwd xrootdSitename: HOUSTON2_INTERNET2_OSDF_CACHE diff --git a/ci/itb-osdf-pelican-cache-values.yaml b/ci/itb-osdf-pelican-cache-values.yaml index 2919b00..8ebe7a4 100644 --- a/ci/itb-osdf-pelican-cache-values.yaml +++ b/ci/itb-osdf-pelican-cache-values.yaml @@ -70,8 +70,9 @@ securityContext: capabilities: add: ["SYS_PTRACE"] -webPasswordSecret: itb-pelican-server-web-passwd -webPasswordSecretKey: server-web-passwd +webPassword: + existingSecret: itb-pelican-server-web-passwd + key: server-web-passwd # Settings not directly parameterized by the chart; merged via extraPelicanConfig. extraPelicanConfig: diff --git a/ci/uw-osdf-cache-values.yaml b/ci/uw-osdf-cache-values.yaml index 4d2b2ef..a063adc 100644 --- a/ci/uw-osdf-cache-values.yaml +++ b/ci/uw-osdf-cache-values.yaml @@ -91,8 +91,9 @@ adminUsers: - "http://cilogon.org/serverA/users/9265706" - "http://cilogon.org/serverE/users/57152" -webPasswordSecret: osdf-prod-pelican-webserver-passwd -webPasswordSecretKey: osdf-pelican-webserver-passwd +webPassword: + existingSecret: osdf-prod-pelican-webserver-passwd + key: osdf-pelican-webserver-passwd xrootd: extraConfig: | diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index 85aff00..25632ef 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -79,7 +79,7 @@ Server: UIAdminUsers: {{- toYaml .Values.adminUsers | nindent 4 }} {{- end }} -{{- if .Values.webPasswordSecret }} +{{- if .Values.webPassword.existingSecret }} UIPasswordFile: /etc/pelican/server-web-passwd {{- end }} @@ -270,8 +270,8 @@ Validate required values and conditional requirements. {{- end }} {{- end }} -{{- if eq (trim (default "" .Values.webPasswordSecret)) "" }} - {{- fail "webPasswordSecret must be nonempty" }} +{{- if eq (trim (default "" .Values.webPassword.existingSecret)) "" }} + {{- fail "webPassword.existingSecret must be nonempty" }} {{- end }} {{- if and .Values.tls.certManager.enabled .Values.tls.existingSecret }} diff --git a/templates/deployment.yaml b/templates/deployment.yaml index e154275..b783325 100644 --- a/templates/deployment.yaml +++ b/templates/deployment.yaml @@ -112,11 +112,11 @@ its own CA there. So we use a different directory here and in the Pelican confi subPath: client.secret readOnly: true {{- end }} - {{- if .Values.webPasswordSecret }} + {{- if .Values.webPassword.existingSecret }} - name: web-passwd readOnly: true mountPath: /etc/pelican/server-web-passwd - subPath: {{ .Values.webPasswordSecretKey | default "server-web-passwd" }} + subPath: {{ .Values.webPassword.key | default "server-web-passwd" }} {{- end }} {{- if .Values.xrootd.extraConfig }} - name: xrootd-config @@ -243,14 +243,14 @@ its own CA there. So we use a different directory here and in the Pelican confi path: client.secret {{- end }} - {{- if .Values.webPasswordSecret }} + {{- if .Values.webPassword.existingSecret }} # Web UI password - name: web-passwd secret: - secretName: {{ .Values.webPasswordSecret }} + secretName: {{ .Values.webPassword.existingSecret }} items: - - key: {{ .Values.webPasswordSecretKey | default "server-web-passwd" }} - path: {{ .Values.webPasswordSecretKey | default "server-web-passwd" }} + - key: {{ .Values.webPassword.key | default "server-web-passwd" }} + path: {{ .Values.webPassword.key | default "server-web-passwd" }} mode: 0600 {{- end }} diff --git a/values.yaml b/values.yaml index ccb5742..c515354 100644 --- a/values.yaml +++ b/values.yaml @@ -175,10 +175,11 @@ oidc: adminUsers: [] # - "http://cilogon.org/serverA/users/12345" -# -- Name of an existing Secret containing the web UI password file -webPasswordSecret: "" -# -- Key within the web password Secret -webPasswordSecretKey: "server-web-passwd" +webPassword: + # -- Name of an existing Secret containing the web UI password file + existingSecret: "" + # -- Key within the web password Secret + key: "server-web-passwd" # -- XRootD configuration xrootd: From 66fcaf75f493d1d8330b0ca7c73b3aa789047582 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Fri, 3 Apr 2026 16:28:44 -0500 Subject: [PATCH 17/37] Disable client X.509 Use of client X.509 certs breaks on many of our caches since Let's Encrypt certificates cannot be used for client auth anymore. Disable them. Co-authored-by: Copilot --- ci/uw-osdf-cache-values.yaml | 4 +--- templates/deployment.yaml | 6 ++++-- values.yaml | 2 -- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/ci/uw-osdf-cache-values.yaml b/ci/uw-osdf-cache-values.yaml index a063adc..b2b4aa9 100644 --- a/ci/uw-osdf-cache-values.yaml +++ b/ci/uw-osdf-cache-values.yaml @@ -112,6 +112,4 @@ securityContext: capabilities: add: ["SYS_PTRACE"] -extraEnv: - - name: XRD_CURLDISABLEX509 - value: "1" +extraEnv: [] diff --git a/templates/deployment.yaml b/templates/deployment.yaml index b783325..81a2e1d 100644 --- a/templates/deployment.yaml +++ b/templates/deployment.yaml @@ -56,10 +56,12 @@ spec: securityContext: {{- toYaml . | nindent 12 }} {{- end }} - {{- if .Values.extraEnv }} env: + - name: XRD_CURLDISABLEX509 + value: "1" + {{- if .Values.extraEnv }} {{- toYaml .Values.extraEnv | nindent 12 }} - {{- end }} + {{- end }} ports: - name: pelican containerPort: 8443 diff --git a/values.yaml b/values.yaml index c515354..d5b4fcf 100644 --- a/values.yaml +++ b/values.yaml @@ -217,8 +217,6 @@ securityContext: {} # -- Extra environment variables for the pelican-cache container extraEnv: [] -# - name: XRD_CURLDISABLEX509 -# value: "1" # -- Extra volumes to add to the pod extraVolumes: [] From aa2cc8ee028fa6c8586a88c74b80f95d6165c22c Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Wed, 1 Apr 2026 20:40:47 -0500 Subject: [PATCH 18/37] Update AGENTS.md Co-authored-by: Copilot --- AGENTS.md | 135 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 117 insertions(+), 18 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 90708c8..ac69f55 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -31,15 +31,20 @@ pelican-cache/ ├── README.md # User-facing documentation ├── AGENTS.md # This file — AI agent context ├── .devcontainer/ -│ └── devcontainer.json # Dev container for chart development +│ ├── devcontainer.json # Dev container for chart development +│ └── post-create.sh # Post-creation setup script ├── ci/ -│ └── uw-osdf-cache-values.yaml # Test values mirroring the real uw-osdf-cache +│ ├── uw-osdf-cache-values.yaml # Test values mirroring the real uw-osdf-cache (hostPath-backed) +│ ├── houston2-i2-pelican-cache-values.yaml # Test values for Nautilus deployment (hostPath-backed) +│ └── itb-osdf-pelican-cache-values.yaml # Test values for ITB federation (PVC-backed) └── templates/ ├── _helpers.tpl # Template helpers: │ # - pelican-cache.fullname │ # - pelican-cache.labels / selectorLabels │ # - pelican-cache.tlsSecretName │ # - pelican-cache.instanceConfig (generates 50-instance.yaml) + │ # - pelican-cache.shouldRenderPvc (avoids PVC adoption conflicts) + │ # - pelican-cache.validateFederation / validateRequiredValues ├── deployment.yaml # Deployment: 2-3 containers, conditional volumes ├── service.yaml # LoadBalancer Service ├── networkpolicy.yaml # NetworkPolicy (conditional) @@ -47,10 +52,11 @@ pelican-cache/ ├── configmap-pelican.yaml # Base + instance Pelican config (pelican.yaml + 50-instance.yaml) ├── configmap-logrotate.yaml # Logrotate configuration ├── configmap-xrootd.yaml # Custom xrootd.conf (conditional) - ├── pvc-cache-data.yaml # Cache data PVC (conditional on storageType=pvc) - ├── pvc-logging.yaml # Logging PVC (always created) - ├── pvc-namespace-key.yaml # Namespace key PVC (conditional on type=pvc) + ├── pvc-cache-data.yaml # Cache data PVC (conditional on type=pvc) + ├── pvc-logging.yaml # Logging PVC (conditional on logging.persistence.separateVolume) + ├── pvc-issuer-key.yaml # Issuer key PVC (conditional on type=pvc) ├── pvc-lotman.yaml # Lotman PVC (conditional on lotman.enabled) + ├── validate.yaml # Render-time validation trigger (no resources emitted) └── NOTES.txt # Post-install notes ``` @@ -60,13 +66,35 @@ pelican-cache/ 2. **Secrets are never chart-managed**: The chart only _references_ pre-existing Secrets. This is intentional — issuer keys, OIDC credentials, TLS certs, and passwords are sensitive and should be managed via SealedSecrets, External Secrets Operator, or manual creation. -3. **Storage flexibility**: Cache data can be PVC or hostPath (production OSDF caches often use hostPath to dedicated NVMe). The namespace/issuer key can be a PVC (Pelican auto-generates the key) or an existing Secret (for key portability across reinstalls). +3. **Storage flexibility with discriminated union pattern**: Cache data uses a `type` discriminator: + - `type: pvc` → uses `cache.pvc.*` settings (supports both creating new PVCs and referencing existing ones via `cache.pvc.existingClaim`) + - `type: hostPath` → uses `cache.hostPath.path` for direct node attachment + - Issuer key similarly uses `issuerKey.type` ("pvc" or "existingSecret") to choose between auto-generated or pre-existing keys. + + **Why discriminated union, not optional `persistence`?** Unlike typical services where persistence is optional, + a cache **requires** persistent storage by design. Using the optional `persistence.enabled` pattern would allow + misconfiguration (e.g., accidentally disabling persistence or omitting storage config). The discriminated union + pattern forces the user to choose WHERE to persist and ensures all required fields for that branch are explicitly set, + preventing silent misconfiguration. 4. **CVMFS redirector defaults to off**: The original base includes it, but the uw-osdf-cache (and most modern deployments) delete it. Defaulting to off matches the common case. -5. **`Recreate` strategy**: The Deployment uses `Recreate` (not `RollingUpdate`) because Pelican holds an exclusive lock on its cache data directory. +5. **Public service exposure by default**: `service.type` defaults to `LoadBalancer` intentionally. This cache is expected to be publicly reachable; `ClusterIP` is not useful for the primary deployment target, and routing through Ingress adds an extra hop that can hurt throughput and latency. -6. **ConfigMap checksum annotations**: The Deployment template includes `sha256sum` checksums of the ConfigMaps as pod annotations, so config changes trigger automatic rollouts. +6. **`Recreate` strategy**: The Deployment uses `Recreate` (not `RollingUpdate`) because Pelican holds an exclusive lock on its cache data directory. + +7. **ConfigMap checksum annotation**: The Deployment template includes a `sha256sum` checksum of the Pelican ConfigMap as a pod annotation (`checksum/pelican-config`), so Pelican config changes trigger automatic rollouts. + +8. **Open ingress policy by default**: The NetworkPolicy allows ingress from any source (on explicit service ports) intentionally. This cache serves federation clients globally, so restrictive source allowlists are not a sensible default. + +9. **Template-time validation**: The chart enforces required values at render time via the `pelican-cache.validateRequiredValues` helper in `_helpers.tpl`. This ensures: + - `serverHostname` is set + - Storage configurations are complete for their type (e.g., `cache.pvc.storageClass` required for new PVCs, but not if using `cache.pvc.existingClaim`) + - Namespace key and logging storage configured appropriately + - Optional features (Lotman, OIDC) have their required secrets/storage when enabled + - Federation label/URL consistency (OSDF and OSDF-ITB have defined pairs) + +10. **Safe PVC rendering with `lookup`**: The PVC templates use the `pelican-cache.shouldRenderPvc` helper to render only when a PVC is absent or already managed by the same Helm release. This avoids Helm trying to adopt unrelated pre-existing PVCs while still allowing upgrades to manage release-owned PVC metadata. ## Pelican-Specific Knowledge @@ -85,18 +113,85 @@ pelican-cache/ ```bash # Lint -helm lint . --set serverHostname=test.example.com +helm lint . --set serverHostname=test.example.com --set sitename=test-site --set cache.pvc.storageClass=std --set logging.persistence.storageClass=std --set issuerKey.pvc.storageClass=std --set webPassword.existingSecret=pw # Render with minimal values -helm template test . --set serverHostname=test.example.com +helm template test . --set serverHostname=test.example.com --set sitename=test-site --set cache.pvc.storageClass=std --set logging.persistence.storageClass=std --set issuerKey.pvc.storageClass=std --set webPassword.existingSecret=pw -# Render with full uw-osdf-cache-equivalent values +# Render with full uw-osdf-cache-equivalent values (hostPath-backed) helm template test . -f ci/uw-osdf-cache-values.yaml -# Verify required value validation -helm template test . # Should fail with "serverHostname is required" +# Render with Houston (hostPath) or ITB (PVC) values +helm template test . -f ci/houston2-i2-pelican-cache-values.yaml +helm template test . -f ci/itb-osdf-pelican-cache-values.yaml + +# Test validation: should fail (first error: cache.pvc.storageClass required since type=pvc is the default) +helm template test . + +# Test validation: should fail with "cache.pvc.storageClass must be nonempty..." +helm template test . --set serverHostname=test.local --set sitename=test-site --set cache.type=pvc --set logging.persistence.storageClass=std --set issuerKey.pvc.storageClass=std --set webPassword.existingSecret=pw + +# Test existingClaim path: should succeed without storageClass +helm template test . -f ci/uw-osdf-cache-values.yaml --set cache.pvc.existingClaim=my-existing-pvc ``` +## Storage Configuration (Discriminated Union Pattern) + +The `cache` and `issuerKey` blocks use a discriminated union pattern controlled by a `type` field: + +### Cache Storage + +```yaml +cache: + type: "pvc" | "hostPath" + + # When type: pvc + pvc: + existingClaim: "" # If set, use this existing PVC; ignores storageClass/size + storageClass: "" # Required if existingClaim is empty; defines StorageClass for new PVC + size: 1000Gi # PVC size (used only when creating a new PVC) + + # When type: hostPath + hostPath: + path: "" # Required; node path to mount +``` + +### Issuer Key Storage + +```yaml +issuerKey: + type: "pvc" | "existingSecret" + + # When type: pvc + pvc: + storageClass: "" # Required; StorageClass for the key PVC + size: 10Mi + + # When type: existingSecret + existingSecret: "" # Required; Secret name containing the issuer key + secretKey: private-key.pem # Key within the Secret +``` + +## Validation Requirements + +The chart enforces these rules at render time: + +| Condition | Requirement | +|-----------|-------------| +| `cache.type == "pvc"` AND `cache.pvc.existingClaim` is empty | `cache.pvc.storageClass` must be nonempty | +| `cache.type == "hostPath"` | `cache.hostPath.path` must be nonempty | +| `issuerKey.type == "pvc"` | `issuerKey.pvc.storageClass` must be nonempty | +| `issuerKey.type == "existingSecret"` | `issuerKey.existingSecret` must be nonempty | +| `logging.persistence.separateVolume == true` AND `logging.persistence.existingClaim` is empty | `logging.persistence.storageClass` must be nonempty | +| `lotman.enabled == true` AND `lotman.pvc.existingClaim` is empty | `lotman.pvc.storageClass` must be nonempty | +| `oidc.enabled == true` | `oidc.existingSecret` must be nonempty | +| Always | `sitename` must be nonempty | +| Always | `webPassword.existingSecret` must be nonempty | +| Always | `serverHostname` must be nonempty | +| Federation consistency | If `federation.discoveryUrl` or `federation.label` match OSDF or OSDF-ITB, both must pair correctly | +| TLS consistency | `tls.certManager.enabled` and `tls.existingSecret` cannot both be set | +| TLS completeness | When `tls.certManager.enabled` is false, `tls.existingSecret` must be nonempty | + ## Common Modification Patterns ### Adding a new Pelican config knob @@ -110,13 +205,17 @@ helm template test . # Should fail with "serverHostname is required" 1. Add an `enabled` toggle in `values.yaml`. 2. Add a `{{- if .Values.newSidecar.enabled }}` block in `deployment.yaml` in the `containers` list. 3. Add any associated volumes, PVCs, or ConfigMaps with the same conditional guard. +4. Update validation rules if the sidecar requires resources or Secrets. + +### Adding support for existing resources (PVC, Secret, etc.) -### Adding a new volume type +Follow the discriminated union pattern: -1. Add values (type, storageClassName, size, existingSecret, etc.) to `values.yaml`. -2. Add conditional PVC template if needed. -3. Add the volume to the `volumes` list in `deployment.yaml`. -4. Add the volumeMount to the appropriate container(s). +1. In `values.yaml`, restructure the relevant block to nest under a key matching the `type` discriminator (e.g., `pvc.*` for PVC config, `secret.*` for Secret config). +2. Add an `existingClaim` or `existingSecret` field to reference pre-existing resources. +3. In templates, conditionally skip resource creation if the existing field is set (e.g., skip PVC if `existingClaim` is populated). +4. In deployment volumes, resolve the claim/secret name correctly based on the discriminator. +5. Add validation rules in `_helpers.tpl` `validateRequiredValues` helper to enforce that required fields are set for each branch. ## Upstream Resources From 41836774eae9cddda5e99f2658cf3fffcf0ffcbd Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Fri, 3 Apr 2026 16:22:19 -0500 Subject: [PATCH 19/37] Set Topology.DisableCacheX509 --- templates/configmap-pelican.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/configmap-pelican.yaml b/templates/configmap-pelican.yaml index 87caf51..6be6088 100644 --- a/templates/configmap-pelican.yaml +++ b/templates/configmap-pelican.yaml @@ -24,5 +24,7 @@ data: Logging: LogLocation: "/var/log/pelican/pelican.log" + Topology.DisableCacheX509: true + 50-instance.yaml: | {{- include "pelican-cache.instanceConfig" . | nindent 4 }} From c6ba320ffe349725687001360abe4d180e2e18e0 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Fri, 17 Apr 2026 11:52:51 -0500 Subject: [PATCH 20/37] Update README file to include missing config options Also drop information about tiger-osg-config - since that's a private repo, the information is of no use to others. Co-authored-by: Copilot --- README.md | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 00ebfc9..f5815c9 100644 --- a/README.md +++ b/README.md @@ -65,11 +65,12 @@ The chart deploys a single Pod with up to three containers: ### Pelican Configuration Layering -Pelican supports loading configuration from multiple files via `ConfigLocations`. This chart now generates a single ConfigMap that contains both layers: +Pelican supports loading configuration from multiple files via `ConfigLocations`. This chart generates a single ConfigMap (`pelican-config`) containing two files: -1. **`pelican-config`** (mounted at `/etc/pelican/pelican.yaml` and `/etc/pelican/config.d/50-instance.yaml`) — Includes fixed infrastructure config (`pelican.yaml`) and generated instance settings (`50-instance.yaml`) from your values: federation URL, hostname, cache tuning, OIDC, Lotman, logging levels, XRootD settings, and any `extraPelicanConfig`. +- **`pelican.yaml`** — Fixed infrastructure config (storage paths, ports, TLS paths). +- **`50-instance.yaml`** — Generated from your values: federation URL, hostname, cache tuning, OIDC, Lotman, logging levels, XRootD settings, and any `extraPelicanConfig`. -Pelican merges these in order, with later files taking precedence. +Both are mounted under `/etc/pelican/` and Pelican merges them in order, with later files taking precedence. ### Storage @@ -78,7 +79,7 @@ The chart manages several persistent volumes: | Volume | Purpose | Backing | |---|---|---| | Cache data | XRootD file cache | PVC or hostPath (`cache.type`) | -| Logging | Pelican log files | PVC (always) | +| Logging | Pelican log files | Dedicated PVC (default) or shared with cache data (`logging.persistence.separateVolume`) | | Issuer key | Pelican issuer/signing key | PVC or existing Secret (`issuerKey.type`) | | Lotman data | Lot-based storage management | PVC (when `lotman.enabled`) | @@ -239,6 +240,8 @@ For details on `Files*Size` parameters, see the [XRootD PFC documentation](https | `logrotate.resources.requests.memory` | `500M` | Logrotate memory request | | `logrotate.resources.limits.cpu` | `2` | Logrotate CPU limit | | `logrotate.resources.limits.memory` | `2G` | Logrotate memory limit | +| `logrotate.size` | `500M` | Log file size threshold that triggers rotation | +| `logrotate.rotate` | `10` | Number of rotated log files to keep | ### Optional Components @@ -287,6 +290,7 @@ through normal chart values. Prefer regular chart parameters when possible. | `extraVolumes` | `[]` | Extra volumes for the pod | | `extraVolumeMounts` | `[]` | Extra volume mounts for the cache container | | `podAnnotations` | `{}` | Extra pod annotations | +| `podSecurityContext` | `{}` | Pod-level security context (fsGroup, runAsUser, etc.) | | `securityContext` | `{}` | Security context for the cache container | ## Examples @@ -307,6 +311,10 @@ logging: persistence: storageClass: standard +issuerKey: + pvc: + storageClass: standard + webPassword: existingSecret: my-web-passwd-secret ``` @@ -385,18 +393,17 @@ securityContext: capabilities: add: ["SYS_PTRACE"] -extraEnv: - - name: XRD_CURLDISABLEX509 - value: "1" ``` ### Non-OSDF Federation ```yaml serverHostname: my-cache.example.com +sitename: MY_CACHE federation: discoveryUrl: "https://my-federation.example.com" + label: my-federation image: # Use the generic Pelican cache image instead of the OSDF-specific one @@ -406,6 +413,8 @@ cache: type: pvc pvc: storageClass: local-nvme + +# ...plus issuerKey, logging, and webPassword settings as shown above ``` ## Validation Requirements @@ -435,8 +444,15 @@ The chart enforces the following validation rules at render time to ensure a val **Web UI:** - `webPassword.existingSecret` must be nonempty -**Federation:** +**Site Identity:** - `serverHostname` must be nonempty +- `sitename` must be nonempty + +**TLS:** +- `tls.certManager.enabled` and `tls.existingSecret` cannot both be set +- When `tls.certManager.enabled` is false, `tls.existingSecret` must be nonempty + +**Federation:** - If `federation.discoveryUrl` or `federation.label` match a known federation pair (OSDF or OSDF-ITB), both must be set consistently ## Secrets Management @@ -466,7 +482,7 @@ The `Recreate` deployment strategy is used (not `RollingUpdate`) because the cac ```bash # Lint the chart -helm lint . --set serverHostname=test.example.com +helm lint . --set serverHostname=test.example.com --set sitename=test-site --set cache.pvc.storageClass=std --set logging.persistence.storageClass=std --set issuerKey.pvc.storageClass=std --set webPassword.existingSecret=pw # Render templates locally helm template my-cache . -f ci/uw-osdf-cache-values.yaml @@ -475,21 +491,6 @@ helm template my-cache . -f ci/uw-osdf-cache-values.yaml helm diff upgrade my-cache . -f my-values.yaml ``` -## Relationship to tiger-osg-config - -This chart is a standalone replacement for the Kustomize + Flux deployment model used in [opensciencegrid/tiger-osg-config](https://github.com/opensciencegrid/tiger-osg-config). The `ci/uw-osdf-cache-values.yaml` file demonstrates a 1:1 mapping from the `uw-osdf-cache` Kustomize overlays to Helm values. - -| Kustomize layer | Helm equivalent | -|---|---| -| `base/pelican-cache/pelican.yaml` | `configmap-pelican.yaml` template (fixed) | -| `base/pelican-cache/deployment.yaml` | `deployment.yaml` template | -| `base/osdf-pelican-cache/10-osdf.yaml` | `federation.discoveryUrl` value | -| Instance `50-instance.yaml` | Generated by `_helpers.tpl` and stored in `configmap-pelican.yaml` | -| Instance `deployment-patch.yaml` | Values: `resources`, `nodeSelector`, `securityContext`, `extraEnv`, etc. | -| Instance `service-patch.yaml` | Values: `service.*` | -| Instance `cert-patch.yaml` | Values: `tls.certManager.dnsNames` | -| Instance PVC patches | Values: `cache.type`, `cache.hostPath.path`, `cache.pvc.*`, `logging.*`, `issuerKey.*`, `lotman.*` | - ## License See repository license. From 173336f0462c7f46ceefbf9617be9c3720152e89 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Fri, 17 Apr 2026 11:57:04 -0500 Subject: [PATCH 21/37] Add host networking; update Houston I2 cache values to more closely resemble the existing cache Co-authored-by: Copilot --- README.md | 7 +++-- ci/houston2-i2-pelican-cache-values.yaml | 34 ++++++++++++++++-------- templates/configmap-pelican.yaml | 4 +-- templates/deployment.yaml | 5 ++-- templates/networkpolicy.yaml | 6 ++--- templates/service.yaml | 6 +++-- values.yaml | 10 +++++++ 7 files changed, 50 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index f5815c9..ef9ff0d 100644 --- a/README.md +++ b/README.md @@ -224,7 +224,7 @@ For details on `Files*Size` parameters, see the [XRootD PFC documentation](https | Parameter | Default | Description | |---|---|---| -| `service.type` | `LoadBalancer` | Service type | +| `service.type` | `LoadBalancer` | Service type (ignored when `hostNetwork` is enabled) | | `service.loadBalancerIP` | `""` | Request a specific LB IP | | `service.externalTrafficPolicy` | `Local` | Traffic policy | | `service.annotations` | `{}` | Extra annotations (external-dns, metallb, etc.) | @@ -248,6 +248,9 @@ For details on `Files*Size` parameters, see the [XRootD PFC documentation](https | Parameter | Default | Description | |---|---|---| | `sleep` | `false` | Debug mode: run `sleep infinity` in the `pelican-cache` container instead of starting the cache process | +| `hostNetwork` | `false` | Use host networking (`spec.hostNetwork=true`) to bind directly to node IP. When enabled, no Service or NetworkPolicy is created. | +| `server.cachePort` | `8443` | Cache port exposed by Pelican and used by container/service/network policy mappings | +| `server.webPort` | `443` | Web UI port exposed by Pelican and container port mapping | | `cvmfsRedirector.enabled` | `false` | Enable CVMFS port redirector sidecar | | `lotman.enabled` | `false` | Enable Lotman (lot-based storage management) | | `lotman.pvc.existingClaim` | `""` | Existing Lotman PVC name (if set, ignores `storageClass` and `size`) | @@ -271,7 +274,7 @@ through normal chart values. Prefer regular chart parameters when possible. | Parameter | Default | Description | |---|---|---| -| `networkPolicy.enabled` | `true` | Create a NetworkPolicy | +| `networkPolicy.enabled` | `true` | Create a NetworkPolicy (ignored when `hostNetwork` is enabled) | ### Scheduling (customization optional) diff --git a/ci/houston2-i2-pelican-cache-values.yaml b/ci/houston2-i2-pelican-cache-values.yaml index 732803e..a716bee 100644 --- a/ci/houston2-i2-pelican-cache-values.yaml +++ b/ci/houston2-i2-pelican-cache-values.yaml @@ -43,9 +43,12 @@ tls: enabled: false existingSecret: houston2-i2-certs -# hostNetwork: true binds directly to the node's network and disables the Service. hostNetwork: true +server: + webPort: 8444 + +# Keep network policy disabled for this hostNetwork-based profile. networkPolicy: enabled: false @@ -66,23 +69,29 @@ logging: cache: Pss: warn Scitokens: warn - origin: - Scitokens: warn - -server: - webPort: 8444 + # NOTE: logging.origin and server.webPort from the real deployment are not + # supported as first-class chart values; use extraPelicanConfig if needed. oidc: enabled: true existingSecret: nrp-oidc-client-secret -adminUsers: "http://cilogon.org/serverE/users/133679 http://cilogon.org/serverA/users/10832 http://cilogon.org/serverA/users/21441 http://cilogon.org/serverA/users/46022246 http://cilogon.org/serverA/users/9265706 http://cilogon.org/serverE/users/57152 http://cilogon.org/serverE/users/130835 http://cilogon.org/serverE/users/245993 http://cilogon.org/serverB/users/51444962" +adminUsers: + - "http://cilogon.org/serverE/users/133679" + - "http://cilogon.org/serverA/users/10832" + - "http://cilogon.org/serverA/users/21441" + - "http://cilogon.org/serverA/users/46022246" + - "http://cilogon.org/serverA/users/9265706" + - "http://cilogon.org/serverE/users/57152" + - "http://cilogon.org/serverE/users/130835" + - "http://cilogon.org/serverE/users/245993" + - "http://cilogon.org/serverB/users/51444962" webPassword: existingSecret: osdf-web-pass-nrp key: server-web-passwd -xrootdSitename: HOUSTON2_INTERNET2_OSDF_CACHE +# NOTE: xrootdSitename is not a chart value; the chart uses "sitename" instead. xrootd: # The real deployment uses an external ConfigMap (xrootdtrt-cfg) for xrootd config. @@ -102,9 +111,8 @@ securityContext: capabilities: add: ["SYS_PTRACE"] -extraEnv: - - name: XRD_CURLDISABLEX509 - value: "1" +# NOTE: XRD_CURLDISABLEX509 is already set unconditionally by the chart's +# deployment template, so it does not need to be repeated in extraEnv. # Convert remaining env-var config that doesn't have first-class values.yaml # knobs into proper YAML config. @@ -114,6 +122,10 @@ extraPelicanConfig: IssuerKey: "/cache/privpelican/issuer.pem" Logging: LogLocation: "/cache/pelican/pelican.log" + Origin: + Scitokens: warn + XrootD: + ConfigFile: "/etc/pelican/xrootdap/xrootdtrt.cfg" Shoveler: Enable: true Topic: "" diff --git a/templates/configmap-pelican.yaml b/templates/configmap-pelican.yaml index 6be6088..1914095 100644 --- a/templates/configmap-pelican.yaml +++ b/templates/configmap-pelican.yaml @@ -14,10 +14,10 @@ data: Cache: StorageLocation: /cache - Port: 8443 + Port: {{ .Values.server.cachePort }} Server: - WebPort: 443 + WebPort: {{ .Values.server.webPort }} TlsCertificate: "/etc/pelican/cert-orig/tls.crt" TlsKey: "/etc/pelican/cert-orig/tls.key" diff --git a/templates/deployment.yaml b/templates/deployment.yaml index 81a2e1d..c65ad7c 100644 --- a/templates/deployment.yaml +++ b/templates/deployment.yaml @@ -22,6 +22,7 @@ spec: {{- include "pelican-cache.labels" . | nindent 8 }} spec: enableServiceLinks: false + hostNetwork: {{ .Values.hostNetwork }} {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} @@ -64,10 +65,10 @@ spec: {{- end }} ports: - name: pelican - containerPort: 8443 + containerPort: {{ .Values.server.cachePort }} protocol: TCP - name: pelican-webui - containerPort: 443 + containerPort: {{ .Values.server.webPort }} protocol: TCP volumeMounts: - name: default-config diff --git a/templates/networkpolicy.yaml b/templates/networkpolicy.yaml index 890da7c..4b5eddb 100644 --- a/templates/networkpolicy.yaml +++ b/templates/networkpolicy.yaml @@ -1,4 +1,4 @@ -{{- if .Values.networkPolicy.enabled }} +{{- if and .Values.networkPolicy.enabled (not .Values.hostNetwork) }} kind: NetworkPolicy apiVersion: networking.k8s.io/v1 metadata: @@ -16,13 +16,13 @@ spec: - from: ports: - protocol: TCP - port: 8443 + port: {{ .Values.server.cachePort }} {{- if .Values.cvmfsRedirector.enabled }} - protocol: TCP port: 8000 {{- end }} - protocol: TCP - port: 443 + port: {{ .Values.server.webPort }} egress: - {} {{- end }} diff --git a/templates/service.yaml b/templates/service.yaml index d7a4750..434677a 100644 --- a/templates/service.yaml +++ b/templates/service.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.hostNetwork }} apiVersion: v1 kind: Service metadata: @@ -21,7 +22,7 @@ spec: ports: - name: pelican protocol: TCP - port: 8443 + port: {{ .Values.server.cachePort }} targetPort: pelican {{- if .Values.cvmfsRedirector.enabled }} - name: pelican-cvmfs @@ -31,5 +32,6 @@ spec: {{- end }} - name: pelican-webui protocol: TCP - port: 443 + port: {{ .Values.server.webPort }} targetPort: pelican-webui + {{- end }} diff --git a/values.yaml b/values.yaml index d5b4fcf..53114d2 100644 --- a/values.yaml +++ b/values.yaml @@ -9,6 +9,13 @@ replicaCount: 1 # -- (Required) External FQDN of the cache server serverHostname: "" +# -- Server process settings +server: + # -- Cache service port + cachePort: 8443 + # -- Web UI HTTPS port + webPort: 443 + # -- (Required) Site name reported to the federation sitename: "" @@ -193,6 +200,9 @@ sleep: false networkPolicy: enabled: true +# -- Use host networking for the pod (matches legacy deployments that bind directly to node IP) +hostNetwork: false + # -- Node selector for pod scheduling nodeSelector: {} From 0bded76ac37f02499ba5951a88f207e44de97286 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Tue, 14 Apr 2026 19:49:13 -0500 Subject: [PATCH 22/37] Bump appVersion and chart version --- Chart.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Chart.yaml b/Chart.yaml index 5b578f7..9f7d331 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: pelican-cache description: A Helm chart for deploying a Pelican Platform cache in the OSDF type: application -version: 0.1.0 -appVersion: "v7.23.0" +version: "0.2.0" +appVersion: "v7.24.2" home: https://pelicanplatform.org sources: - https://github.com/PelicanPlatform/pelican From 8fc3e093724a513471522af41ad859acc701f971 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Thu, 23 Apr 2026 13:37:16 -0500 Subject: [PATCH 23/37] Remove XRD_CURLDISABLEX509 from the environment The relevant bug was fixed in 7.23.1 and 7.24: https://github.com/PelicanPlatform/pelican/issues/3159 Co-authored-by: Claude --- templates/deployment.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/templates/deployment.yaml b/templates/deployment.yaml index c65ad7c..ecbf332 100644 --- a/templates/deployment.yaml +++ b/templates/deployment.yaml @@ -57,12 +57,10 @@ spec: securityContext: {{- toYaml . | nindent 12 }} {{- end }} + {{- if .Values.extraEnv }} env: - - name: XRD_CURLDISABLEX509 - value: "1" - {{- if .Values.extraEnv }} {{- toYaml .Values.extraEnv | nindent 12 }} - {{- end }} + {{- end }} ports: - name: pelican containerPort: {{ .Values.server.cachePort }} From f7f7fc564693f4837156bdab89771b4f97547e38 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Fri, 24 Apr 2026 13:55:45 -0500 Subject: [PATCH 24/37] Various README wording changes from code review --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ef9ff0d..df999ba 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ Your options are: | Parameter | Default | Description | |---|---|---| -| `adminUsers` | `[]` | List of CILogon admin user identities | +| `adminUsers` | `[]` | List of OIDC `sub` claims of users that should have admin access | | `webPassword.existingSecret` | `""` | Existing Secret for web UI password | | `webPassword.key` | `server-web-passwd` | Key within the web password Secret | @@ -222,9 +222,11 @@ For details on `Files*Size` parameters, see the [XRootD PFC documentation](https ### Service (customization optional) +Note: When `hostNetwork` is enabled, a Service does not get created, in which case these settings have no effect. + | Parameter | Default | Description | |---|---|---| -| `service.type` | `LoadBalancer` | Service type (ignored when `hostNetwork` is enabled) | +| `service.type` | `LoadBalancer` | Service type | | `service.loadBalancerIP` | `""` | Request a specific LB IP | | `service.externalTrafficPolicy` | `Local` | Traffic policy | | `service.annotations` | `{}` | Extra annotations (external-dns, metallb, etc.) | @@ -272,9 +274,11 @@ through normal chart values. Prefer regular chart parameters when possible. ### Network Policy +Note: When `hostNetwork` is enabled, a NetworkPolicy does not get created, regardless of the setting of `networkPolicy.enabled`. + | Parameter | Default | Description | |---|---|---| -| `networkPolicy.enabled` | `true` | Create a NetworkPolicy (ignored when `hostNetwork` is enabled) | +| `networkPolicy.enabled` | `true` | Create a NetworkPolicy | ### Scheduling (customization optional) From fd195d8e6420cbde2e1dfb3abf4da3c3d4cc4444 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Fri, 24 Apr 2026 14:39:37 -0500 Subject: [PATCH 25/37] Move validation notes from README.md to values.yaml --- README.md | 29 +---------------------------- values.yaml | 43 ++++++++++++++++++++++++++++--------------- 2 files changed, 29 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index df999ba..741319d 100644 --- a/README.md +++ b/README.md @@ -202,7 +202,7 @@ not configurable. | `cache.concurrency` | `""` | Max concurrent requests (e.g. `240` — guidance: 10× core count) | | `cache.highWaterMark` | `""` | Eviction high watermark (e.g. `27000g`) | | `cache.lowWaterMark` | `""` | Eviction low watermark (e.g. `25000g`) | -| `cache.filesMaxSize` | `""` | XRootD diskusage max tracked size. Must be < low watermark. | +| `cache.filesMaxSize` | `""` | XRootD diskusage max tracked size. Must be < low watermark. | | `cache.filesNominalSize` | `""` | XRootD diskusage nominal tracked size | | `cache.filesBaseSize` | `""` | XRootD diskusage base tracked size | @@ -428,37 +428,10 @@ cache: The chart enforces the following validation rules at render time to ensure a valid configuration: -**Storage:** -- If `cache.type` is `pvc`: - - If `cache.pvc.existingClaim` is empty, `cache.pvc.storageClass` must be nonempty - - If `cache.pvc.existingClaim` is set, `storageClass` and `size` are ignored -- If `cache.type` is `hostPath`: - - `cache.hostPath.path` must be nonempty - **Issuer Key:** - If `issuerKey.type` is `pvc`, `issuerKey.pvc.storageClass` must be nonempty - If `issuerKey.type` is `existingSecret`, `issuerKey.existingSecret` must be nonempty -**Logging:** -- If `logging.persistence.separateVolume` is `true` and `logging.persistence.existingClaim` is empty, `logging.persistence.storageClass` must be nonempty - -**Lotman:** -- If `lotman.enabled` is `true` and `lotman.pvc.existingClaim` is empty, `lotman.pvc.storageClass` must be nonempty - -**OIDC:** -- If `oidc.enabled` is `true`, `oidc.existingSecret` must be nonempty - -**Web UI:** -- `webPassword.existingSecret` must be nonempty - -**Site Identity:** -- `serverHostname` must be nonempty -- `sitename` must be nonempty - -**TLS:** -- `tls.certManager.enabled` and `tls.existingSecret` cannot both be set -- When `tls.certManager.enabled` is false, `tls.existingSecret` must be nonempty - **Federation:** - If `federation.discoveryUrl` or `federation.label` match a known federation pair (OSDF or OSDF-ITB), both must be set consistently diff --git a/values.yaml b/values.yaml index 53114d2..3627ccf 100644 --- a/values.yaml +++ b/values.yaml @@ -67,15 +67,18 @@ cache: type: pvc # -- Host path configuration when type is "hostPath" hostPath: - # -- Host path (e.g. /srv/pelican-cache/) + # -- Host path (e.g. /srv/pelican-cache/); this must be specified. path: "" # -- PVC configuration when type is "pvc" pvc: - # -- Name of an existing PVC to use (if set, storageClass and size are ignored) + # -- Name of an existing PVC to use for cache data; if not set, will create + # -- a new PVC using the storageClass and size parameters below. existingClaim: "" - # -- Storage class name for creating a new PVC + # -- Storage class name for creating a new PVC; this must be specified + # -- unless you are using existingClaim. storageClass: "" - # -- PVC size when creating a new PVC + # -- PVC size when creating a new PVC; this must be specified + # -- unless you are using existingClaim. size: 1000Gi # -- Number of blocks to prefetch (e.g. 10) blocksToPrefetch: "" @@ -113,7 +116,8 @@ issuerKey: tls: certManager: - # -- Create a cert-manager Certificate resource + # -- Create a cert-manager Certificate resource. If true, you must fill + # -- out the issuerRef section and leave existingSecret blank. enabled: true issuerRef: # -- Name of the ClusterIssuer or Issuer @@ -122,7 +126,8 @@ tls: kind: ClusterIssuer # -- Additional DNS Subject Alternative Names for the certificate (serverHostname is always included) dnsNames: [] - # -- Use an existing TLS Secret instead of cert-manager + # -- Use an existing TLS Secret instead of cert-manager; + # -- if this is set, tls.certManager.enabled must be false. existingSecret: "" # -- Service configuration @@ -143,11 +148,14 @@ logging: # -- on the cache disk (e.g. via extraPelicanConfig.Logging.LogLocation). # -- Logrotate always runs. separateVolume: true - # -- Name of an existing PVC to use for logs (if set, storageClass and size are ignored) + # -- Name of an existing PVC to use for logs; if not set, will create + # -- a new PVC using the storageClass and size parameters below. existingClaim: "" - # -- Storage class for creating a new logging PVC + # -- Storage class for creating a new logging PVC; this must be specified + # -- unless you are using existingClaim. storageClass: "" - # -- Logging PVC size when creating a new PVC + # -- Logging PVC size when creating a new PVC; this must be specified + # -- unless you are using existingClaim. size: 50Gi # -- Global log level level: INFO @@ -162,18 +170,23 @@ logging: # -- Lotman (lot-based storage management) lotman: + # -- Enable lotman for the cache. You must fill out the pvc section below + # -- if this is true. enabled: false pvc: - # -- Name of an existing PVC to use for lotman data (if set, storageClass and size are ignored) + # -- Name of an existing PVC to use for lotman data; if not set, will create + # -- a new PVC using the storageClass and size parameters below. existingClaim: "" - # -- Storage class for creating a new lotman PVC + # -- Storage class for creating a new lotman PVC; this must be specified + # -- unless you are using existingClaim. storageClass: "" - # -- Lotman PVC size when creating a new PVC + # -- Lotman PVC size when creating a new PVC; this must be specified + # -- unless you are using existingClaim. size: 10Gi # -- OIDC authentication oidc: - # -- Enable OIDC for the cache + # -- Enable OIDC for the cache. You must specify existingSecret if this is true. enabled: false # -- Name of an existing Secret with keys "client.id" and "client.secret" existingSecret: "" @@ -183,7 +196,7 @@ adminUsers: [] # - "http://cilogon.org/serverA/users/12345" webPassword: - # -- Name of an existing Secret containing the web UI password file + # -- Required: Name of an existing Secret containing the web UI password file existingSecret: "" # -- Key within the web password Secret key: "server-web-passwd" @@ -200,7 +213,7 @@ sleep: false networkPolicy: enabled: true -# -- Use host networking for the pod (matches legacy deployments that bind directly to node IP) +# -- Use host networking for the pod hostNetwork: false # -- Node selector for pod scheduling From 3e2fad61bfc4927cf7f6510b9c6b56d4e5f9d848 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Fri, 24 Apr 2026 15:08:42 -0500 Subject: [PATCH 26/37] Require a secret for the issuer key instead of allowing use of a PVC. Co-authored-by: Copilot --- AGENTS.md | 24 ++++++------------- README.md | 30 +++++++----------------- ci/houston2-i2-pelican-cache-values.yaml | 1 - ci/itb-osdf-pelican-cache-values.yaml | 1 - ci/uw-osdf-cache-values.yaml | 1 - templates/NOTES.txt | 6 ----- templates/_helpers.tpl | 13 ++-------- templates/deployment.yaml | 13 +--------- templates/pvc-issuer-key.yaml | 21 ----------------- values.yaml | 9 +------ 10 files changed, 19 insertions(+), 100 deletions(-) delete mode 100644 templates/pvc-issuer-key.yaml diff --git a/AGENTS.md b/AGENTS.md index ac69f55..f29fdcb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -54,7 +54,6 @@ pelican-cache/ ├── configmap-xrootd.yaml # Custom xrootd.conf (conditional) ├── pvc-cache-data.yaml # Cache data PVC (conditional on type=pvc) ├── pvc-logging.yaml # Logging PVC (conditional on logging.persistence.separateVolume) - ├── pvc-issuer-key.yaml # Issuer key PVC (conditional on type=pvc) ├── pvc-lotman.yaml # Lotman PVC (conditional on lotman.enabled) ├── validate.yaml # Render-time validation trigger (no resources emitted) └── NOTES.txt # Post-install notes @@ -69,7 +68,7 @@ pelican-cache/ 3. **Storage flexibility with discriminated union pattern**: Cache data uses a `type` discriminator: - `type: pvc` → uses `cache.pvc.*` settings (supports both creating new PVCs and referencing existing ones via `cache.pvc.existingClaim`) - `type: hostPath` → uses `cache.hostPath.path` for direct node attachment - - Issuer key similarly uses `issuerKey.type` ("pvc" or "existingSecret") to choose between auto-generated or pre-existing keys. + - Issuer key is always sourced from a pre-existing Secret via `issuerKey.existingSecret`. **Why discriminated union, not optional `persistence`?** Unlike typical services where persistence is optional, a cache **requires** persistent storage by design. Using the optional `persistence.enabled` pattern would allow @@ -101,7 +100,7 @@ pelican-cache/ - **Pelican** is a data federation platform built on XRootD. A "cache" is a read-through caching proxy. - **OSDF** = Open Science Data Federation, the primary Pelican federation run by OSG. - **Federation Discovery URL** (`https://osg-htc.org`) tells Pelican where to find the OSDF director and registry. -- **IssuerKey** is a JWK used to sign tokens. When using a PVC, Pelican auto-generates it on first start. When using an existing Secret, the operator provides a pre-generated key. +- **IssuerKey** is a JWK used to sign tokens. The chart expects operators to pre-generate it and provide it via an existing Secret. - **Lotman** = lot-based storage management (experimental). Manages disk quotas per "lot." - **CVMFS port redirector** = a sidecar that redirects legacy CVMFS clients (port 8000) to the Pelican cache. - **Cache.StorageLocation**: In Pelican 7.12+, `StorageLocation` is the preferred cache path setting. @@ -113,10 +112,10 @@ pelican-cache/ ```bash # Lint -helm lint . --set serverHostname=test.example.com --set sitename=test-site --set cache.pvc.storageClass=std --set logging.persistence.storageClass=std --set issuerKey.pvc.storageClass=std --set webPassword.existingSecret=pw +helm lint . --set serverHostname=test.example.com --set sitename=test-site --set cache.pvc.storageClass=std --set logging.persistence.storageClass=std --set issuerKey.existingSecret=issuer-key --set webPassword.existingSecret=pw # Render with minimal values -helm template test . --set serverHostname=test.example.com --set sitename=test-site --set cache.pvc.storageClass=std --set logging.persistence.storageClass=std --set issuerKey.pvc.storageClass=std --set webPassword.existingSecret=pw +helm template test . --set serverHostname=test.example.com --set sitename=test-site --set cache.pvc.storageClass=std --set logging.persistence.storageClass=std --set issuerKey.existingSecret=issuer-key --set webPassword.existingSecret=pw # Render with full uw-osdf-cache-equivalent values (hostPath-backed) helm template test . -f ci/uw-osdf-cache-values.yaml @@ -129,7 +128,7 @@ helm template test . -f ci/itb-osdf-pelican-cache-values.yaml helm template test . # Test validation: should fail with "cache.pvc.storageClass must be nonempty..." -helm template test . --set serverHostname=test.local --set sitename=test-site --set cache.type=pvc --set logging.persistence.storageClass=std --set issuerKey.pvc.storageClass=std --set webPassword.existingSecret=pw +helm template test . --set serverHostname=test.local --set sitename=test-site --set cache.type=pvc --set logging.persistence.storageClass=std --set issuerKey.existingSecret=issuer-key --set webPassword.existingSecret=pw # Test existingClaim path: should succeed without storageClass helm template test . -f ci/uw-osdf-cache-values.yaml --set cache.pvc.existingClaim=my-existing-pvc @@ -137,7 +136,7 @@ helm template test . -f ci/uw-osdf-cache-values.yaml --set cache.pvc.existingCla ## Storage Configuration (Discriminated Union Pattern) -The `cache` and `issuerKey` blocks use a discriminated union pattern controlled by a `type` field: +The `cache` block uses a discriminated union pattern controlled by a `type` field: ### Cache Storage @@ -160,14 +159,6 @@ cache: ```yaml issuerKey: - type: "pvc" | "existingSecret" - - # When type: pvc - pvc: - storageClass: "" # Required; StorageClass for the key PVC - size: 10Mi - - # When type: existingSecret existingSecret: "" # Required; Secret name containing the issuer key secretKey: private-key.pem # Key within the Secret ``` @@ -180,8 +171,7 @@ The chart enforces these rules at render time: |-----------|-------------| | `cache.type == "pvc"` AND `cache.pvc.existingClaim` is empty | `cache.pvc.storageClass` must be nonempty | | `cache.type == "hostPath"` | `cache.hostPath.path` must be nonempty | -| `issuerKey.type == "pvc"` | `issuerKey.pvc.storageClass` must be nonempty | -| `issuerKey.type == "existingSecret"` | `issuerKey.existingSecret` must be nonempty | +| Always | `issuerKey.existingSecret` must be nonempty | | `logging.persistence.separateVolume == true` AND `logging.persistence.existingClaim` is empty | `logging.persistence.storageClass` must be nonempty | | `lotman.enabled == true` AND `lotman.pvc.existingClaim` is empty | `lotman.pvc.storageClass` must be nonempty | | `oidc.enabled == true` | `oidc.existingSecret` must be nonempty | diff --git a/README.md b/README.md index 741319d..16c2191 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ helm install my-cache ./pelican-cache \ --set sitename=my-site \ --set cache.pvc.storageClass=my-storage-class \ --set logging.persistence.storageClass=my-storage-class \ - --set issuerKey.pvc.storageClass=my-storage-class \ + --set issuerKey.existingSecret=my-issuer-key-secret \ --set webPassword.existingSecret=my-web-passwd-secret ``` @@ -80,7 +80,7 @@ The chart manages several persistent volumes: |---|---|---| | Cache data | XRootD file cache | PVC or hostPath (`cache.type`) | | Logging | Pelican log files | Dedicated PVC (default) or shared with cache data (`logging.persistence.separateVolume`) | -| Issuer key | Pelican issuer/signing key | PVC or existing Secret (`issuerKey.type`) | +| Issuer key | Pelican issuer/signing key | Existing Secret (`issuerKey.existingSecret`) | | Lotman data | Lot-based storage management | PVC (when `lotman.enabled`) | **NVMe storage is strongly recommended for the cache data volume.** @@ -124,22 +124,11 @@ If using a host path, you must specify the path in `cache.hostPath.path`. | Parameter | Default | Description | |---|---|---| -| `issuerKey.type` | `pvc` | `pvc` (Pelican auto-generates) or `existingSecret` | -| `issuerKey.pvc.storageClass` | `""` | StorageClass for key PVC | -| `issuerKey.pvc.size` | `10Mi` | Key PVC size | -| `issuerKey.existingSecret` | `""` | Pre-existing Secret name (when type is `existingSecret`) | +| `issuerKey.existingSecret` | `""` | Pre-existing Secret name containing the issuer key | | `issuerKey.secretKey` | `private-key.pem` | Key within the Secret | -You must specify a way to store the issuer key (which is the key Pelican uses to sign credentials -and authenticate itself to the federation). -Your options are: - -1. Have Pelican generate the key and save it to a PVC that the chart creates. - To do this, specify `issuerKey.type: pvc` and `issuerKey.pvc.storageClass` to one of the available storage types in your cluster. - PVCs created by this chart will not be deleted on uninstall. - -2. Pre-create the key using the `pelican key create` command, and save it as a secret. - To do this, specify `issuerKey.type: existingSecret` and specify the secret name as `issuerKey.existingSecret`. +You must pre-create the issuer key using `pelican key create` and save it in a Secret. +Set `issuerKey.existingSecret` to that Secret name. ### Logging (customization required) @@ -319,8 +308,7 @@ logging: storageClass: standard issuerKey: - pvc: - storageClass: standard + existingSecret: my-issuer-key-secret webPassword: existingSecret: my-web-passwd-secret @@ -349,7 +337,6 @@ cache: memory: "48Gi" issuerKey: - type: existingSecret existingSecret: my-cache-issuer-key tls: @@ -429,8 +416,7 @@ cache: The chart enforces the following validation rules at render time to ensure a valid configuration: **Issuer Key:** -- If `issuerKey.type` is `pvc`, `issuerKey.pvc.storageClass` must be nonempty -- If `issuerKey.type` is `existingSecret`, `issuerKey.existingSecret` must be nonempty +- `issuerKey.existingSecret` must be nonempty **Federation:** - If `federation.discoveryUrl` or `federation.label` match a known federation pair (OSDF or OSDF-ITB), both must be set consistently @@ -462,7 +448,7 @@ The `Recreate` deployment strategy is used (not `RollingUpdate`) because the cac ```bash # Lint the chart -helm lint . --set serverHostname=test.example.com --set sitename=test-site --set cache.pvc.storageClass=std --set logging.persistence.storageClass=std --set issuerKey.pvc.storageClass=std --set webPassword.existingSecret=pw +helm lint . --set serverHostname=test.example.com --set sitename=test-site --set cache.pvc.storageClass=std --set logging.persistence.storageClass=std --set issuerKey.existingSecret=issuer-key --set webPassword.existingSecret=pw # Render templates locally helm template my-cache . -f ci/uw-osdf-cache-values.yaml diff --git a/ci/houston2-i2-pelican-cache-values.yaml b/ci/houston2-i2-pelican-cache-values.yaml index a716bee..2973ea3 100644 --- a/ci/houston2-i2-pelican-cache-values.yaml +++ b/ci/houston2-i2-pelican-cache-values.yaml @@ -31,7 +31,6 @@ cache: memory: "210Gi" issuerKey: - type: existingSecret existingSecret: houston2-i2-pelican-pelican-cache-key secretKey: issuer.pem diff --git a/ci/itb-osdf-pelican-cache-values.yaml b/ci/itb-osdf-pelican-cache-values.yaml index 8ebe7a4..b070e8d 100644 --- a/ci/itb-osdf-pelican-cache-values.yaml +++ b/ci/itb-osdf-pelican-cache-values.yaml @@ -27,7 +27,6 @@ cache: memory: "16Gi" issuerKey: - type: existingSecret existingSecret: itb-osdf-pelican-cache-issuer-keys secretKey: issuer.pem diff --git a/ci/uw-osdf-cache-values.yaml b/ci/uw-osdf-cache-values.yaml index b2b4aa9..a54cf70 100644 --- a/ci/uw-osdf-cache-values.yaml +++ b/ci/uw-osdf-cache-values.yaml @@ -32,7 +32,6 @@ cache: memory: "48Gi" issuerKey: - type: existingSecret existingSecret: uw-osdf-cache-issuer-key secretKey: issuer.pem diff --git a/templates/NOTES.txt b/templates/NOTES.txt index 233d91c..b971471 100644 --- a/templates/NOTES.txt +++ b/templates/NOTES.txt @@ -38,12 +38,6 @@ For more information: https://docs.pelicanplatform.org/ {{- $createdPVCs = append $createdPVCs $loggingPvcName -}} {{- end -}} -{{- $issuerKeyPvcName := printf "%s-issuer-key" $fullname -}} -{{- $issuerKeyShouldRender := eq (include "pelican-cache.shouldRenderPvc" (dict "root" . "pvcName" $issuerKeyPvcName) | trim) "true" -}} -{{- if and (eq .Values.issuerKey.type "pvc") $issuerKeyShouldRender -}} -{{- $createdPVCs = append $createdPVCs $issuerKeyPvcName -}} -{{- end -}} - {{- $lotmanPvcName := printf "%s-lotman" $fullname -}} {{- $lotmanShouldRender := eq (include "pelican-cache.shouldRenderPvc" (dict "root" . "pvcName" $lotmanPvcName) | trim) "true" -}} {{- if and .Values.lotman.enabled (not .Values.lotman.pvc.existingClaim) $lotmanShouldRender -}} diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index 25632ef..606c9d9 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -215,7 +215,6 @@ Validate required values and conditional requirements. */}} {{- define "pelican-cache.validateRequiredValues" -}} {{- $cacheStorageType := .Values.cache.type | default "" }} -{{- $issuerKeyType := .Values.issuerKey.type | default "" }} {{- $loggingPersist := .Values.logging.persistence.separateVolume }} {{- if eq $cacheStorageType "pvc" }} @@ -232,16 +231,8 @@ Validate required values and conditional requirements. {{- end }} {{- end }} -{{- if eq $issuerKeyType "pvc" }} - {{- if eq (trim (default "" .Values.issuerKey.pvc.storageClass)) "" }} - {{- fail "issuerKey.pvc.storageClass must be nonempty when issuerKey.type is \"pvc\"" }} - {{- end }} -{{- end }} - -{{- if eq $issuerKeyType "existingSecret" }} - {{- if eq (trim (default "" .Values.issuerKey.existingSecret)) "" }} - {{- fail "issuerKey.existingSecret must be nonempty when issuerKey.type is \"existingSecret\"" }} - {{- end }} +{{- if eq (trim (default "" .Values.issuerKey.existingSecret)) "" }} + {{- fail "issuerKey.existingSecret must be nonempty" }} {{- end }} {{- if eq (trim (default "" .Values.sitename)) "" }} diff --git a/templates/deployment.yaml b/templates/deployment.yaml index ecbf332..e92d56f 100644 --- a/templates/deployment.yaml +++ b/templates/deployment.yaml @@ -84,15 +84,10 @@ spec: - name: data mountPath: /var/log {{- end }} - {{- if eq .Values.issuerKey.type "existingSecret" }} - name: issuer-key readOnly: true mountPath: /etc/pelican/issuer-keys/issuer.pem subPath: {{ .Values.issuerKey.secretKey }} - {{- else }} - - name: issuer-key - mountPath: /etc/pelican/issuer-keys - {{- end }} - name: tls-cert {{/* We cannot mount the host cert secret at /etc/pelican/certificates because that would make that directory read-only and Pelican needs to write @@ -211,19 +206,13 @@ its own CA there. So we use a different directory here and in the Pelican confi {{- end }} # Issuer key - {{- if eq .Values.issuerKey.type "existingSecret" }} - name: issuer-key secret: - secretName: {{ required "issuerKey.existingSecret is required when type is existingSecret" .Values.issuerKey.existingSecret }} + secretName: {{ required "issuerKey.existingSecret is required" .Values.issuerKey.existingSecret }} items: - key: {{ .Values.issuerKey.secretKey }} path: {{ .Values.issuerKey.secretKey }} mode: 0600 - {{- else }} - - name: issuer-key - persistentVolumeClaim: - claimName: {{ include "pelican-cache.fullname" . }}-issuer-key - {{- end }} {{- if .Values.lotman.enabled }} # Lotman persistent data diff --git a/templates/pvc-issuer-key.yaml b/templates/pvc-issuer-key.yaml deleted file mode 100644 index 221d54b..0000000 --- a/templates/pvc-issuer-key.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- $pvcName := printf "%s-issuer-key" (include "pelican-cache.fullname" .) -}} -{{- $shouldRender := eq (include "pelican-cache.shouldRenderPvc" (dict "root" . "pvcName" $pvcName) | trim) "true" -}} -{{- if and (eq .Values.issuerKey.type "pvc") $shouldRender }} -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: {{ $pvcName }} - annotations: - helm.sh/resource-policy: keep - labels: - {{- include "pelican-cache.labels" . | nindent 4 }} -spec: - {{- if .Values.issuerKey.pvc.storageClass }} - storageClassName: {{ .Values.issuerKey.pvc.storageClass | quote }} - {{- end }} - accessModes: - - ReadWriteOnce - resources: - requests: - storage: {{ .Values.issuerKey.pvc.size }} -{{- end }} diff --git a/values.yaml b/values.yaml index 3627ccf..504ca40 100644 --- a/values.yaml +++ b/values.yaml @@ -102,14 +102,7 @@ cache: # -- Issuer key configuration issuerKey: - # -- Key source type: "pvc" (auto-generated by Pelican) or "existingSecret" - type: pvc - pvc: - # -- Storage class for the issuer key PVC - storageClass: "" - # -- PVC size - size: 10Mi - # -- Name of an existing Secret containing the issuer key (when type is "existingSecret") + # -- Required: Name of an existing Secret containing the issuer key existingSecret: "" # -- Key within the Secret that contains the issuer key file secretKey: "private-key.pem" From f31f0fad930a1d29e559f792d6736443c3f1640d Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Fri, 24 Apr 2026 15:40:35 -0500 Subject: [PATCH 27/37] Use the federation discoveryURL as the label instead of requiring a separate label value that must match it This simplifies configuration and processing since we no longer need a separate validation for it. Note that the label needs to be sanitized because `:` and `/` are not allowed in labels. While we're at it, add the `pelicanplatform.org/` prefix to the label, because unprefixed labels should be reserved for the local cluster admin. Co-authored-by: Copilot --- AGENTS.md | 6 +-- README.md | 15 +----- ci/houston2-i2-pelican-cache-values.yaml | 1 - ci/itb-osdf-pelican-cache-values.yaml | 1 - ci/uw-osdf-cache-values.yaml | 1 - templates/_helpers.tpl | 59 +++++++----------------- templates/validate.yaml | 1 - values.yaml | 2 - 8 files changed, 19 insertions(+), 67 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index f29fdcb..63b734b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,7 +17,7 @@ manifests/tiger/osdf-prod/uw-osdf-cache/ That deployment uses a **three-layer Kustomize inheritance chain**: 1. `manifests/base/pelican-cache/` — Generic Pelican cache base (Deployment with 3 containers: `pelican-cache`, `cvmfs-redirector`, `logrotate`; Service; 3 PVCs; NetworkPolicy; cert-manager Certificate; ConfigMaps for pelican.yaml and logrotate) -2. `manifests/base/osdf-pelican-cache/` — OSDF overlay (adds `osdf-` namePrefix, `federation: osdf` label, sets `Federation.DiscoveryUrl` to `https://osg-htc.org`, replaces overlay-config emptyDir with a ConfigMap) +2. `manifests/base/osdf-pelican-cache/` — OSDF overlay (adds `osdf-` namePrefix, `pelicanplatform.org/federation: https://osg-htc.org` label, sets `Federation.DiscoveryUrl` to `https://osg-htc.org`, replaces overlay-config emptyDir with a ConfigMap) 3. `manifests/tiger/osdf-prod/uw-osdf-cache/` — Instance overlay (pins images, pins to a specific node, deletes cvmfs-redirector, replaces cache-data PVC with hostPath, replaces namespace-key PVC with a Secret from SealedSecret, adds lotman/OIDC/web-password volumes, sets heavy resources and cache tuning) This chart collapses all three layers into parameterized Helm templates. @@ -44,7 +44,7 @@ pelican-cache/ │ # - pelican-cache.tlsSecretName │ # - pelican-cache.instanceConfig (generates 50-instance.yaml) │ # - pelican-cache.shouldRenderPvc (avoids PVC adoption conflicts) - │ # - pelican-cache.validateFederation / validateRequiredValues + │ # - pelican-cache.validateRequiredValues ├── deployment.yaml # Deployment: 2-3 containers, conditional volumes ├── service.yaml # LoadBalancer Service ├── networkpolicy.yaml # NetworkPolicy (conditional) @@ -91,7 +91,6 @@ pelican-cache/ - Storage configurations are complete for their type (e.g., `cache.pvc.storageClass` required for new PVCs, but not if using `cache.pvc.existingClaim`) - Namespace key and logging storage configured appropriately - Optional features (Lotman, OIDC) have their required secrets/storage when enabled - - Federation label/URL consistency (OSDF and OSDF-ITB have defined pairs) 10. **Safe PVC rendering with `lookup`**: The PVC templates use the `pelican-cache.shouldRenderPvc` helper to render only when a PVC is absent or already managed by the same Helm release. This avoids Helm trying to adopt unrelated pre-existing PVCs while still allowing upgrades to manage release-owned PVC metadata. @@ -178,7 +177,6 @@ The chart enforces these rules at render time: | Always | `sitename` must be nonempty | | Always | `webPassword.existingSecret` must be nonempty | | Always | `serverHostname` must be nonempty | -| Federation consistency | If `federation.discoveryUrl` or `federation.label` match OSDF or OSDF-ITB, both must pair correctly | | TLS consistency | `tls.certManager.enabled` and `tls.existingSecret` cannot both be set | | TLS completeness | When `tls.certManager.enabled` is false, `tls.existingSecret` must be nonempty | diff --git a/README.md b/README.md index 16c2191..84f8c50 100644 --- a/README.md +++ b/README.md @@ -157,17 +157,8 @@ and specify that as `webPassword.existingSecret`. | Parameter | Default | Description | |---|---|---| | `federation.discoveryUrl` | `https://osg-htc.org` | Federation discovery URL. Change for non-OSDF or ITB. | -| `federation.label` | `osdf` | Resource label indicating the federation. This must match the discovery URL when set to known values. | -The federation label and discovery URL have to match for the OSDF and OSDF-ITB federations. -The valid pairs are: - -| discoveryUrl | label | -|---|---| -| https://osg-htc.org | osdf | -| https://osdf-itb.osg-htc.org | osdf-itb | - -Checks are not performed for other federations. +All resources are labelled with `pelicanplatform.org/federation` set to the sanitized value of `federation.discoveryUrl` (scheme and special characters removed). The default federation is OSDF so OSDF caches do not need to change this section. ### Images (customization optional) @@ -397,7 +388,6 @@ sitename: MY_CACHE federation: discoveryUrl: "https://my-federation.example.com" - label: my-federation image: # Use the generic Pelican cache image instead of the OSDF-specific one @@ -418,9 +408,6 @@ The chart enforces the following validation rules at render time to ensure a val **Issuer Key:** - `issuerKey.existingSecret` must be nonempty -**Federation:** -- If `federation.discoveryUrl` or `federation.label` match a known federation pair (OSDF or OSDF-ITB), both must be set consistently - ## Secrets Management This chart **does not create Secrets**. All sensitive material must be provisioned separately before installing the chart. Common approaches: diff --git a/ci/houston2-i2-pelican-cache-values.yaml b/ci/houston2-i2-pelican-cache-values.yaml index 2973ea3..10663d7 100644 --- a/ci/houston2-i2-pelican-cache-values.yaml +++ b/ci/houston2-i2-pelican-cache-values.yaml @@ -6,7 +6,6 @@ sitename: HOUSTON2_INTERNET2_OSDF_CACHE federation: discoveryUrl: "https://osg-htc.org" - label: osdf image: repository: hub.opensciencegrid.org/pelican_platform/osdf-cache diff --git a/ci/itb-osdf-pelican-cache-values.yaml b/ci/itb-osdf-pelican-cache-values.yaml index b070e8d..58898f5 100644 --- a/ci/itb-osdf-pelican-cache-values.yaml +++ b/ci/itb-osdf-pelican-cache-values.yaml @@ -6,7 +6,6 @@ sitename: ITB-OSDF-PELICAN-CACHE federation: discoveryUrl: "https://osdf-itb.osg-htc.org" - label: osdf-itb image: repository: hub.opensciencegrid.org/pelican_platform/osdf-cache diff --git a/ci/uw-osdf-cache-values.yaml b/ci/uw-osdf-cache-values.yaml index a54cf70..12bf164 100644 --- a/ci/uw-osdf-cache-values.yaml +++ b/ci/uw-osdf-cache-values.yaml @@ -6,7 +6,6 @@ sitename: CHTC_PELICAN_CACHE federation: discoveryUrl: "https://osg-htc.org" - label: osdf image: repository: hub.opensciencegrid.org/pelican_platform/osdf-cache diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index 606c9d9..7b02166 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -35,9 +35,7 @@ Common labels {{- define "pelican-cache.labels" -}} helm.sh/chart: {{ include "pelican-cache.chart" . }} {{ include "pelican-cache.selectorLabels" . }} -{{- if .Values.federation.label }} -federation: {{ .Values.federation.label }} -{{- end }} +pelicanplatform.org/federation: {{ include "pelican-cache.sanitizedFederationUrl" . | quote }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} @@ -52,6 +50,21 @@ app.kubernetes.io/name: {{ include "pelican-cache.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} +{{/* +Sanitize federation discovery URL for use as a label value. + +Kubernetes label values must be alphanumeric or contain `-`, `_`, or `.`. +This helper: +1. Strips any URI scheme (e.g., `https://`, `http://`, `ftp://`, etc.) +2. Replaces `/` and `:` with `_` +*/}} +{{- define "pelican-cache.sanitizedFederationUrl" -}} +{{- $url := .Values.federation.discoveryUrl }} +{{- $stripped := regexReplaceAll "^[^:]+://" $url "" -}} +{{- $stripped := regexReplaceAll "[^A-Za-z0-9._-]" $stripped "_" -}} +{{- $stripped }} +{{- end }} + {{/* TLS Secret name - either from cert-manager Certificate or an existing Secret */}} @@ -68,7 +81,6 @@ Generate the Pelican instance configuration (50-instance.yaml content). This is the user-configurable layer that Pelican loads from /etc/pelican/config.d/. */}} {{- define "pelican-cache.instanceConfig" -}} -{{- include "pelican-cache.validateFederation" . }} --- Federation: DiscoveryUrl: {{ .Values.federation.discoveryUrl | quote }} @@ -171,45 +183,6 @@ false {{- end -}} {{- end }} -{{/* -Validate that federation.discoveryUrl and federation.label are consistent. -The two known federations each require a specific pairing: - - https://osg-htc.org <-> osdf - - https://osdf-itb.osg-htc.org <-> osdf-itb -If either value in a pair is set to one of the known values, the other must -match exactly. -*/}} -{{- define "pelican-cache.validateFederation" -}} -{{- $url := .Values.federation.discoveryUrl | default "" }} -{{- $label := .Values.federation.label | default "" }} - -{{- /* url → required label */}} -{{- if eq $url "https://osg-htc.org" }} - {{- if and $label (ne $label "osdf") }} - {{- fail (printf "federation.discoveryUrl is %q but federation.label is %q — it must be \"osdf\"" $url $label) }} - {{- end }} -{{- end }} - -{{- if eq $url "https://osdf-itb.osg-htc.org" }} - {{- if and $label (ne $label "osdf-itb") }} - {{- fail (printf "federation.discoveryUrl is %q but federation.label is %q — it must be \"osdf-itb\"" $url $label) }} - {{- end }} -{{- end }} - -{{- /* label → required url */}} -{{- if eq $label "osdf" }} - {{- if and $url (ne $url "https://osg-htc.org") }} - {{- fail (printf "federation.label is %q but federation.discoveryUrl is %q — it must be \"https://osg-htc.org\"" $label $url) }} - {{- end }} -{{- end }} - -{{- if eq $label "osdf-itb" }} - {{- if and $url (ne $url "https://osdf-itb.osg-htc.org") }} - {{- fail (printf "federation.label is %q but federation.discoveryUrl is %q — it must be \"https://osdf-itb.osg-htc.org\"" $label $url) }} - {{- end }} -{{- end }} -{{- end }} - {{/* Validate required values and conditional requirements. */}} diff --git a/templates/validate.yaml b/templates/validate.yaml index c0bac3e..4e26ce9 100644 --- a/templates/validate.yaml +++ b/templates/validate.yaml @@ -2,5 +2,4 @@ This template exists solely to trigger validation checks on every render. It produces no Kubernetes resources — the file is empty after rendering. */}} -{{- include "pelican-cache.validateFederation" . }} {{- include "pelican-cache.validateRequiredValues" . }} diff --git a/values.yaml b/values.yaml index 504ca40..3432e02 100644 --- a/values.yaml +++ b/values.yaml @@ -23,8 +23,6 @@ sitename: "" federation: # -- Discovery URL for the federation (OSDF default) discoveryUrl: "https://osg-htc.org" - # -- Federation label applied to all resources (e.g. "osdf") - label: "osdf" # -- Cache container image image: From 2ec52ace81e89d21d80fa5d00e93755ad13b3aaf Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Fri, 24 Apr 2026 15:52:59 -0500 Subject: [PATCH 28/37] Drop the CVMFS forwarder Co-authored-by: Copilot --- AGENTS.md | 19 ++++++++----------- README.md | 4 +--- ci/houston2-i2-pelican-cache-values.yaml | 3 --- ci/uw-osdf-cache-values.yaml | 3 --- templates/deployment.yaml | 14 -------------- templates/networkpolicy.yaml | 4 ---- templates/service.yaml | 6 ------ values.yaml | 12 ------------ 8 files changed, 9 insertions(+), 56 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 63b734b..808a380 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,9 +16,9 @@ manifests/tiger/osdf-prod/uw-osdf-cache/ That deployment uses a **three-layer Kustomize inheritance chain**: -1. `manifests/base/pelican-cache/` — Generic Pelican cache base (Deployment with 3 containers: `pelican-cache`, `cvmfs-redirector`, `logrotate`; Service; 3 PVCs; NetworkPolicy; cert-manager Certificate; ConfigMaps for pelican.yaml and logrotate) +1. `manifests/base/pelican-cache/` — Generic Pelican cache base (Deployment with 2 containers: `pelican-cache`, `logrotate`; Service; 3 PVCs; NetworkPolicy; cert-manager Certificate; ConfigMaps for pelican.yaml and logrotate) 2. `manifests/base/osdf-pelican-cache/` — OSDF overlay (adds `osdf-` namePrefix, `pelicanplatform.org/federation: https://osg-htc.org` label, sets `Federation.DiscoveryUrl` to `https://osg-htc.org`, replaces overlay-config emptyDir with a ConfigMap) -3. `manifests/tiger/osdf-prod/uw-osdf-cache/` — Instance overlay (pins images, pins to a specific node, deletes cvmfs-redirector, replaces cache-data PVC with hostPath, replaces namespace-key PVC with a Secret from SealedSecret, adds lotman/OIDC/web-password volumes, sets heavy resources and cache tuning) +3. `manifests/tiger/osdf-prod/uw-osdf-cache/` — Instance overlay (pins images, pins to a specific node, replaces cache-data PVC with hostPath, replaces namespace-key PVC with a Secret from SealedSecret, adds lotman/OIDC/web-password volumes, sets heavy resources and cache tuning) This chart collapses all three layers into parameterized Helm templates. @@ -45,7 +45,7 @@ pelican-cache/ │ # - pelican-cache.instanceConfig (generates 50-instance.yaml) │ # - pelican-cache.shouldRenderPvc (avoids PVC adoption conflicts) │ # - pelican-cache.validateRequiredValues - ├── deployment.yaml # Deployment: 2-3 containers, conditional volumes + ├── deployment.yaml # Deployment: 2 containers, conditional volumes ├── service.yaml # LoadBalancer Service ├── networkpolicy.yaml # NetworkPolicy (conditional) ├── certificate.yaml # cert-manager Certificate (conditional) @@ -76,23 +76,21 @@ pelican-cache/ pattern forces the user to choose WHERE to persist and ensures all required fields for that branch are explicitly set, preventing silent misconfiguration. -4. **CVMFS redirector defaults to off**: The original base includes it, but the uw-osdf-cache (and most modern deployments) delete it. Defaulting to off matches the common case. +4. **`Recreate` strategy**: The Deployment uses `Recreate` (not `RollingUpdate`) because Pelican holds an exclusive lock on its cache data directory. 5. **Public service exposure by default**: `service.type` defaults to `LoadBalancer` intentionally. This cache is expected to be publicly reachable; `ClusterIP` is not useful for the primary deployment target, and routing through Ingress adds an extra hop that can hurt throughput and latency. -6. **`Recreate` strategy**: The Deployment uses `Recreate` (not `RollingUpdate`) because Pelican holds an exclusive lock on its cache data directory. +6. **ConfigMap checksum annotation**: The Deployment template includes a `sha256sum` checksum of the Pelican ConfigMap as a pod annotation (`checksum/pelican-config`), so Pelican config changes trigger automatic rollouts. -7. **ConfigMap checksum annotation**: The Deployment template includes a `sha256sum` checksum of the Pelican ConfigMap as a pod annotation (`checksum/pelican-config`), so Pelican config changes trigger automatic rollouts. +7. **Open ingress policy by default**: The NetworkPolicy allows ingress from any source (on explicit service ports) intentionally. This cache serves federation clients globally, so restrictive source allowlists are not a sensible default. -8. **Open ingress policy by default**: The NetworkPolicy allows ingress from any source (on explicit service ports) intentionally. This cache serves federation clients globally, so restrictive source allowlists are not a sensible default. - -9. **Template-time validation**: The chart enforces required values at render time via the `pelican-cache.validateRequiredValues` helper in `_helpers.tpl`. This ensures: +8. **Template-time validation**: The chart enforces required values at render time via the `pelican-cache.validateRequiredValues` helper in `_helpers.tpl`. This ensures: - `serverHostname` is set - Storage configurations are complete for their type (e.g., `cache.pvc.storageClass` required for new PVCs, but not if using `cache.pvc.existingClaim`) - Namespace key and logging storage configured appropriately - Optional features (Lotman, OIDC) have their required secrets/storage when enabled -10. **Safe PVC rendering with `lookup`**: The PVC templates use the `pelican-cache.shouldRenderPvc` helper to render only when a PVC is absent or already managed by the same Helm release. This avoids Helm trying to adopt unrelated pre-existing PVCs while still allowing upgrades to manage release-owned PVC metadata. +9. **Safe PVC rendering with `lookup`**: The PVC templates use the `pelican-cache.shouldRenderPvc` helper to render only when a PVC is absent or already managed by the same Helm release. This avoids Helm trying to adopt unrelated pre-existing PVCs while still allowing upgrades to manage release-owned PVC metadata. ## Pelican-Specific Knowledge @@ -101,7 +99,6 @@ pelican-cache/ - **Federation Discovery URL** (`https://osg-htc.org`) tells Pelican where to find the OSDF director and registry. - **IssuerKey** is a JWK used to sign tokens. The chart expects operators to pre-generate it and provide it via an existing Secret. - **Lotman** = lot-based storage management (experimental). Manages disk quotas per "lot." -- **CVMFS port redirector** = a sidecar that redirects legacy CVMFS clients (port 8000) to the Pelican cache. - **Cache.StorageLocation**: In Pelican 7.12+, `StorageLocation` is the preferred cache path setting. - **`Cache.HighWaterMark` / `LowWaterMark`**: XRootD's cache eviction thresholds. When total disk usage crosses the high watermark, files are evicted until it drops below the low watermark. - **`Files*Size` parameters**: Fine-grained diskusage tracking specific to the mount where cache data lives. `FilesMaxSize` must be lower than the low water mark. diff --git a/README.md b/README.md index 84f8c50..3fb6d6d 100644 --- a/README.md +++ b/README.md @@ -55,13 +55,12 @@ See [ci/uw-osdf-cache-values.yaml](ci/uw-osdf-cache-values.yaml) for a complete ## Architecture -The chart deploys a single Pod with up to three containers: +The chart deploys a single Pod with two containers: | Container | Purpose | Optional | |---|---|---| | `pelican-cache` | Main Pelican cache process | No | | `logrotate` | Rotates Pelican log files | No | -| `cvmfs-redirector` | CVMFS port redirector sidecar | Yes (`cvmfsRedirector.enabled`) | ### Pelican Configuration Layering @@ -233,7 +232,6 @@ Note: When `hostNetwork` is enabled, a Service does not get created, in which ca | `hostNetwork` | `false` | Use host networking (`spec.hostNetwork=true`) to bind directly to node IP. When enabled, no Service or NetworkPolicy is created. | | `server.cachePort` | `8443` | Cache port exposed by Pelican and used by container/service/network policy mappings | | `server.webPort` | `443` | Web UI port exposed by Pelican and container port mapping | -| `cvmfsRedirector.enabled` | `false` | Enable CVMFS port redirector sidecar | | `lotman.enabled` | `false` | Enable Lotman (lot-based storage management) | | `lotman.pvc.existingClaim` | `""` | Existing Lotman PVC name (if set, ignores `storageClass` and `size`) | | `lotman.pvc.storageClass` | `""` | StorageClass for new Lotman PVC (required if `existingClaim` is not set and `enabled=true`) | diff --git a/ci/houston2-i2-pelican-cache-values.yaml b/ci/houston2-i2-pelican-cache-values.yaml index 10663d7..c7c46df 100644 --- a/ci/houston2-i2-pelican-cache-values.yaml +++ b/ci/houston2-i2-pelican-cache-values.yaml @@ -12,9 +12,6 @@ image: tag: "v7.22.0" pullPolicy: IfNotPresent -cvmfsRedirector: - enabled: false - # The real deployment uses an existing PVC (pvc-dtn-pas.hous.net.internet2.edu). cache: type: hostPath diff --git a/ci/uw-osdf-cache-values.yaml b/ci/uw-osdf-cache-values.yaml index 12bf164..a7fcc72 100644 --- a/ci/uw-osdf-cache-values.yaml +++ b/ci/uw-osdf-cache-values.yaml @@ -11,9 +11,6 @@ image: repository: hub.opensciencegrid.org/pelican_platform/osdf-cache tag: "v7.23.0" -cvmfsRedirector: - enabled: false - cache: type: hostPath hostPath: diff --git a/templates/deployment.yaml b/templates/deployment.yaml index e92d56f..3710685 100644 --- a/templates/deployment.yaml +++ b/templates/deployment.yaml @@ -123,20 +123,6 @@ its own CA there. So we use a different directory here and in the Pelican confi {{- toYaml . | nindent 12 }} {{- end }} - {{- if .Values.cvmfsRedirector.enabled }} - # ---- CVMFS port redirector sidecar ---- - - name: cvmfs-redirector - image: "{{ .Values.cvmfsRedirector.image.repository }}:{{ .Values.cvmfsRedirector.image.tag }}" - imagePullPolicy: Always - ports: - - name: pelican-cvmfs - containerPort: 8000 - {{- with .Values.cvmfsRedirector.resources }} - resources: - {{- toYaml . | nindent 12 }} - {{- end }} - {{- end }} - # ---- Logrotate sidecar ---- - name: logrotate image: "{{ .Values.logrotate.image.repository }}:{{ .Values.logrotate.image.tag }}" diff --git a/templates/networkpolicy.yaml b/templates/networkpolicy.yaml index 4b5eddb..f1c117d 100644 --- a/templates/networkpolicy.yaml +++ b/templates/networkpolicy.yaml @@ -17,10 +17,6 @@ spec: ports: - protocol: TCP port: {{ .Values.server.cachePort }} - {{- if .Values.cvmfsRedirector.enabled }} - - protocol: TCP - port: 8000 - {{- end }} - protocol: TCP port: {{ .Values.server.webPort }} egress: diff --git a/templates/service.yaml b/templates/service.yaml index 434677a..d2f3338 100644 --- a/templates/service.yaml +++ b/templates/service.yaml @@ -24,12 +24,6 @@ spec: protocol: TCP port: {{ .Values.server.cachePort }} targetPort: pelican - {{- if .Values.cvmfsRedirector.enabled }} - - name: pelican-cvmfs - protocol: TCP - port: 8000 - targetPort: pelican-cvmfs - {{- end }} - name: pelican-webui protocol: TCP port: {{ .Values.server.webPort }} diff --git a/values.yaml b/values.yaml index 3432e02..b539d2c 100644 --- a/values.yaml +++ b/values.yaml @@ -45,18 +45,6 @@ logrotate: size: 500M rotate: 10 -# -- CVMFS port redirector sidecar -cvmfsRedirector: - # -- Enable the CVMFS port redirector container - enabled: false - image: - repository: hub.opensciencegrid.org/pelican_platform/port-redirector - tag: latest - resources: - requests: - memory: "1Gi" - cpu: "1" - # -- Cache data storage configuration # -- You must choose between "pvc" and "hostPath" to determine where to # -- persist, and then configure the details in the appropriate subsections. From 8cee124e233fdb531105efbf6f641cb29bfc7040 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Fri, 24 Apr 2026 16:13:26 -0500 Subject: [PATCH 29/37] Merge pelican.yaml and 50-instance.yaml into a single config file Also move the definition out of `_helpers.tpl` since it's only used in one place. Co-authored-by: Copilot --- AGENTS.md | 3 +- README.md | 9 ++-- templates/_helpers.tpl | 78 -------------------------------- templates/configmap-pelican.yaml | 78 ++++++++++++++++++++++++++++---- templates/deployment.yaml | 20 +++----- 5 files changed, 81 insertions(+), 107 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 808a380..87e5427 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -42,7 +42,6 @@ pelican-cache/ │ # - pelican-cache.fullname │ # - pelican-cache.labels / selectorLabels │ # - pelican-cache.tlsSecretName - │ # - pelican-cache.instanceConfig (generates 50-instance.yaml) │ # - pelican-cache.shouldRenderPvc (avoids PVC adoption conflicts) │ # - pelican-cache.validateRequiredValues ├── deployment.yaml # Deployment: 2 containers, conditional volumes @@ -61,7 +60,7 @@ pelican-cache/ ## Key Design Decisions -1. **Pelican config layering**: One ConfigMap (`pelican-config`) contains both `pelican.yaml` (infrastructure paths) and `50-instance.yaml` (user settings). Pelican's `ConfigLocations` directive merges the files in order. +1. **Single Pelican config file**: One ConfigMap (`pelican-config`) contains a single `config.yaml` file, mounted at `/etc/pelican/config.d/config.yaml`. It combines all settings: infrastructure (storage paths, ports, TLS) and user-configurable settings (federation URL, hostname, cache tuning, OIDC, Lotman, logging levels, XRootD settings). Generated by the `pelican-cache.config` helper in `_helpers.tpl`. 2. **Secrets are never chart-managed**: The chart only _references_ pre-existing Secrets. This is intentional — issuer keys, OIDC credentials, TLS certs, and passwords are sensitive and should be managed via SealedSecrets, External Secrets Operator, or manual creation. diff --git a/README.md b/README.md index 3fb6d6d..9d78046 100644 --- a/README.md +++ b/README.md @@ -62,14 +62,11 @@ The chart deploys a single Pod with two containers: | `pelican-cache` | Main Pelican cache process | No | | `logrotate` | Rotates Pelican log files | No | -### Pelican Configuration Layering +### Pelican Configuration -Pelican supports loading configuration from multiple files via `ConfigLocations`. This chart generates a single ConfigMap (`pelican-config`) containing two files: +This chart generates a single ConfigMap (`pelican-config`) containing one file: -- **`pelican.yaml`** — Fixed infrastructure config (storage paths, ports, TLS paths). -- **`50-instance.yaml`** — Generated from your values: federation URL, hostname, cache tuning, OIDC, Lotman, logging levels, XRootD settings, and any `extraPelicanConfig`. - -Both are mounted under `/etc/pelican/` and Pelican merges them in order, with later files taking precedence. +- **`config.yaml`** — Mounted at `/etc/pelican/config.d/config.yaml`. Contains all settings: infrastructure config (storage paths, ports, TLS paths) and user-configurable settings (federation URL, hostname, cache tuning, OIDC, Lotman, logging levels, XRootD settings, and any `extraPelicanConfig`). ### Storage diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index 7b02166..2ad16a8 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -76,84 +76,6 @@ TLS Secret name - either from cert-manager Certificate or an existing Secret {{- end }} {{- end }} -{{/* -Generate the Pelican instance configuration (50-instance.yaml content). -This is the user-configurable layer that Pelican loads from /etc/pelican/config.d/. -*/}} -{{- define "pelican-cache.instanceConfig" -}} ---- -Federation: - DiscoveryUrl: {{ .Values.federation.discoveryUrl | quote }} - -Server: - Hostname: {{ required "serverHostname is required" .Values.serverHostname | quote }} -{{- if .Values.adminUsers }} - UIAdminUsers: - {{- toYaml .Values.adminUsers | nindent 4 }} -{{- end }} -{{- if .Values.webPassword.existingSecret }} - UIPasswordFile: /etc/pelican/server-web-passwd -{{- end }} - -{{- if .Values.cache.blocksToPrefetch }} -Cache.BlocksToPrefetch: {{ .Values.cache.blocksToPrefetch }} -{{- end }} -{{- if .Values.oidc.enabled }} -Cache.EnableOIDC: true -{{- end }} -{{- if .Values.lotman.enabled }} -Cache.EnableLotman: true -{{- end }} -{{- if .Values.cache.highWaterMark }} -Cache.HighWaterMark: {{ .Values.cache.highWaterMark }} -{{- end }} -{{- if .Values.cache.lowWaterMark }} -Cache.LowWaterMark: {{ .Values.cache.lowWaterMark }} -{{- end }} -{{- if .Values.cache.filesMaxSize }} -Cache.FilesMaxSize: {{ .Values.cache.filesMaxSize }} -{{- end }} -{{- if .Values.cache.filesNominalSize }} -Cache.FilesNominalSize: {{ .Values.cache.filesNominalSize }} -{{- end }} -{{- if .Values.cache.filesBaseSize }} -Cache.FilesBaseSize: {{ .Values.cache.filesBaseSize }} -{{- end }} -{{- if .Values.cache.concurrency }} -Cache.Concurrency: {{ .Values.cache.concurrency }} -{{- end }} - -{{- if .Values.oidc.enabled }} - -OIDC: - ClientIDFile: /etc/pelican/oidc/client.id - ClientSecretFile: /etc/pelican/oidc/client.secret -{{- end }} - -Logging: - Level: {{ .Values.logging.level | quote }} -{{- if .Values.logging.cache }} - Cache: -{{ toYaml .Values.logging.cache | indent 4 }} -{{- end }} - -{{- if .Values.lotman.enabled }} - -Lotman: - LotHome: /var/lib/pelican/lotman -{{- end }} - -XrootD.Sitename: {{ required "sitename is required" .Values.sitename | quote }} -{{- if .Values.xrootd.extraConfig }} -XrootD.ConfigFile: /etc/pelican/xrootd.conf -{{- end }} - -{{- if .Values.extraPelicanConfig }} - -{{ toYaml .Values.extraPelicanConfig }} -{{- end }} -{{- end }} - {{/* Return true when a PVC should be rendered by this chart. diff --git a/templates/configmap-pelican.yaml b/templates/configmap-pelican.yaml index 1914095..ae1db74 100644 --- a/templates/configmap-pelican.yaml +++ b/templates/configmap-pelican.yaml @@ -5,26 +5,88 @@ metadata: labels: {{- include "pelican-cache.labels" . | nindent 4 }} data: -{{/*See note in deployment.yaml for why we have to change TlsCertificate and TlsKey*/}} - pelican.yaml: | + config.yaml: | --- - ConfigLocations: - - "/usr/share/pelican/config.d" - - "/etc/pelican/config.d" - Cache: StorageLocation: /cache Port: {{ .Values.server.cachePort }} + Federation: + DiscoveryUrl: {{ .Values.federation.discoveryUrl | quote }} + Server: WebPort: {{ .Values.server.webPort }} +{{/* We cannot mount the TLS secret at /etc/pelican/certificates because that +directory would become read-only and Pelican needs to write its own CA there. +So we mount it at cert-orig instead (see deployment.yaml). */}} TlsCertificate: "/etc/pelican/cert-orig/tls.crt" TlsKey: "/etc/pelican/cert-orig/tls.key" + Hostname: {{ required "serverHostname is required" .Values.serverHostname | quote }} + {{- if .Values.adminUsers }} + UIAdminUsers: + {{- toYaml .Values.adminUsers | nindent 8 }} + {{- end }} + {{- if .Values.webPassword.existingSecret }} + UIPasswordFile: /etc/pelican/server-web-passwd + {{- end }} + + {{- if .Values.cache.blocksToPrefetch }} + Cache.BlocksToPrefetch: {{ .Values.cache.blocksToPrefetch }} + {{- end }} + {{- if .Values.oidc.enabled }} + Cache.EnableOIDC: true + {{- end }} + {{- if .Values.lotman.enabled }} + Cache.EnableLotman: true + {{- end }} + {{- if .Values.cache.highWaterMark }} + Cache.HighWaterMark: {{ .Values.cache.highWaterMark }} + {{- end }} + {{- if .Values.cache.lowWaterMark }} + Cache.LowWaterMark: {{ .Values.cache.lowWaterMark }} + {{- end }} + {{- if .Values.cache.filesMaxSize }} + Cache.FilesMaxSize: {{ .Values.cache.filesMaxSize }} + {{- end }} + {{- if .Values.cache.filesNominalSize }} + Cache.FilesNominalSize: {{ .Values.cache.filesNominalSize }} + {{- end }} + {{- if .Values.cache.filesBaseSize }} + Cache.FilesBaseSize: {{ .Values.cache.filesBaseSize }} + {{- end }} + {{- if .Values.cache.concurrency }} + Cache.Concurrency: {{ .Values.cache.concurrency }} + {{- end }} + + {{- if .Values.oidc.enabled }} + + OIDC: + ClientIDFile: /etc/pelican/oidc/client.id + ClientSecretFile: /etc/pelican/oidc/client.secret + {{- end }} Logging: LogLocation: "/var/log/pelican/pelican.log" + Level: {{ .Values.logging.level | quote }} + {{- if .Values.logging.cache }} + Cache: + {{- toYaml .Values.logging.cache | nindent 8 }} + {{- end }} Topology.DisableCacheX509: true - 50-instance.yaml: | -{{- include "pelican-cache.instanceConfig" . | nindent 4 }} + {{- if .Values.lotman.enabled }} + + Lotman: + LotHome: /var/lib/pelican/lotman + {{- end }} + + XrootD.Sitename: {{ required "sitename is required" .Values.sitename | quote }} + {{- if .Values.xrootd.extraConfig }} + XrootD.ConfigFile: /etc/pelican/xrootd.conf + {{- end }} + + {{- if .Values.extraPelicanConfig }} + + {{ toYaml .Values.extraPelicanConfig }} + {{- end }} diff --git a/templates/deployment.yaml b/templates/deployment.yaml index 3710685..1b190aa 100644 --- a/templates/deployment.yaml +++ b/templates/deployment.yaml @@ -69,12 +69,9 @@ spec: containerPort: {{ .Values.server.webPort }} protocol: TCP volumeMounts: - - name: default-config - mountPath: /etc/pelican/pelican.yaml - subPath: pelican.yaml - - name: default-config - mountPath: /etc/pelican/config.d/50-instance.yaml - subPath: 50-instance.yaml + - name: pelican-config + mountPath: /etc/pelican/config.d/config.yaml + subPath: config.yaml - name: data mountPath: /cache {{- if .Values.logging.persistence.separateVolume }} @@ -155,16 +152,13 @@ its own CA there. So we use a different directory here and in the Pelican confi path: tls.key mode: 0600 - # Pelican configuration (base + instance settings) - - name: default-config + # Pelican configuration + - name: pelican-config configMap: name: {{ include "pelican-cache.fullname" . }}-pelican-config items: - - key: pelican.yaml - path: pelican.yaml - mode: 0644 - - key: 50-instance.yaml - path: 50-instance.yaml + - key: config.yaml + path: config.yaml mode: 0644 # Logrotate configuration From c0adcc8bba8e9c52c2c0899724d36c88f2fcba53 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Mon, 27 Apr 2026 17:10:45 -0500 Subject: [PATCH 30/37] Move the cache image settings under `cache.` for consistency with the logrotate image settings Also use the `pelican_platform/cache` image instead of the `pelican_platform/osdf-cache` image to be more generic. Co-authored-by: Copilot --- README.md | 17 ++++++++--------- ci/houston2-i2-pelican-cache-values.yaml | 9 ++++----- ci/itb-osdf-pelican-cache-values.yaml | 7 +++---- ci/uw-osdf-cache-values.yaml | 7 +++---- templates/deployment.yaml | 4 ++-- values.yaml | 11 +++++------ 6 files changed, 25 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 9d78046..8fc59b9 100644 --- a/README.md +++ b/README.md @@ -161,9 +161,9 @@ The default federation is OSDF so OSDF caches do not need to change this section | Parameter | Default | Description | |---|---|---| -| `image.repository` | `hub.opensciencegrid.org/pelican_platform/osdf-cache` | Cache container image | -| `image.tag` | `""` | Cache image tag (defaults to chart `appVersion` when empty) | -| `image.pullPolicy` | `IfNotPresent` | Image pull policy for the cache container | +| `cache.image.repository` | `hub.opensciencegrid.org/pelican_platform/cache` | Cache container image | +| `cache.image.tag` | `""` | Cache image tag (defaults to chart `appVersion` when empty) | +| `cache.image.pullPolicy` | `IfNotPresent` | Image pull policy for the cache container | | `logrotate.image.repository` | `hub.opensciencegrid.org/opensciencegrid/logrotate` | Logrotate sidecar image | | `logrotate.image.tag` | `24-release` | Logrotate image tag | @@ -306,10 +306,9 @@ webPassword: serverHostname: osdf-uw-cache.svc.osg-htc.org sitename: MY_PELICAN_CACHE -image: - tag: "v7.23.0" - cache: + image: + tag: "v7.23.0" type: hostPath hostPath: path: /srv/pelican-cache/ @@ -384,11 +383,11 @@ sitename: MY_CACHE federation: discoveryUrl: "https://my-federation.example.com" -image: - # Use the generic Pelican cache image instead of the OSDF-specific one - repository: hub.opensciencegrid.org/pelican_platform/cache cache: + image: + # Use the generic Pelican cache image instead of the OSDF-specific one + repository: hub.opensciencegrid.org/pelican_platform/cache type: pvc pvc: storageClass: local-nvme diff --git a/ci/houston2-i2-pelican-cache-values.yaml b/ci/houston2-i2-pelican-cache-values.yaml index c7c46df..6b3b3ee 100644 --- a/ci/houston2-i2-pelican-cache-values.yaml +++ b/ci/houston2-i2-pelican-cache-values.yaml @@ -7,13 +7,12 @@ sitename: HOUSTON2_INTERNET2_OSDF_CACHE federation: discoveryUrl: "https://osg-htc.org" -image: - repository: hub.opensciencegrid.org/pelican_platform/osdf-cache - tag: "v7.22.0" - pullPolicy: IfNotPresent - # The real deployment uses an existing PVC (pvc-dtn-pas.hous.net.internet2.edu). cache: + image: + repository: hub.opensciencegrid.org/pelican_platform/osdf-cache + tag: "v7.22.0" + pullPolicy: IfNotPresent type: hostPath hostPath: path: /cache diff --git a/ci/itb-osdf-pelican-cache-values.yaml b/ci/itb-osdf-pelican-cache-values.yaml index 58898f5..28fdbe0 100644 --- a/ci/itb-osdf-pelican-cache-values.yaml +++ b/ci/itb-osdf-pelican-cache-values.yaml @@ -7,14 +7,13 @@ sitename: ITB-OSDF-PELICAN-CACHE federation: discoveryUrl: "https://osdf-itb.osg-htc.org" -image: - repository: hub.opensciencegrid.org/pelican_platform/osdf-cache - tag: "v7.23.0" - cvmfsRedirector: enabled: false cache: + image: + repository: hub.opensciencegrid.org/pelican_platform/osdf-cache + tag: "v7.23.0" type: pvc pvc: storageClass: 3x-replica-hdd-raddus diff --git a/ci/uw-osdf-cache-values.yaml b/ci/uw-osdf-cache-values.yaml index a7fcc72..5337c0b 100644 --- a/ci/uw-osdf-cache-values.yaml +++ b/ci/uw-osdf-cache-values.yaml @@ -7,11 +7,10 @@ sitename: CHTC_PELICAN_CACHE federation: discoveryUrl: "https://osg-htc.org" -image: - repository: hub.opensciencegrid.org/pelican_platform/osdf-cache - tag: "v7.23.0" - cache: + image: + repository: hub.opensciencegrid.org/pelican_platform/osdf-cache + tag: "v7.23.0" type: hostPath hostPath: path: /srv/pelican-cache/ diff --git a/templates/deployment.yaml b/templates/deployment.yaml index 1b190aa..297ec3a 100644 --- a/templates/deployment.yaml +++ b/templates/deployment.yaml @@ -42,13 +42,13 @@ spec: containers: # ---- Main Pelican cache container ---- - name: pelican-cache - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + image: "{{ .Values.cache.image.repository }}:{{ .Values.cache.image.tag | default .Chart.AppVersion }}" {{- if .Values.sleep }} command: ["sleep", "infinity"] {{- else }} args: [] {{- end }} - imagePullPolicy: {{ .Values.image.pullPolicy }} + imagePullPolicy: {{ .Values.cache.image.pullPolicy }} {{- with .Values.cache.resources }} resources: {{- toYaml . | nindent 12 }} diff --git a/values.yaml b/values.yaml index b539d2c..295d8ff 100644 --- a/values.yaml +++ b/values.yaml @@ -24,12 +24,6 @@ federation: # -- Discovery URL for the federation (OSDF default) discoveryUrl: "https://osg-htc.org" -# -- Cache container image -image: - repository: hub.opensciencegrid.org/pelican_platform/osdf-cache - tag: "" - pullPolicy: IfNotPresent - # -- Logrotate sidecar configuration logrotate: image: @@ -49,6 +43,11 @@ logrotate: # -- You must choose between "pvc" and "hostPath" to determine where to # -- persist, and then configure the details in the appropriate subsections. cache: + # -- Cache container image + image: + repository: hub.opensciencegrid.org/pelican_platform/cache + tag: "" + pullPolicy: IfNotPresent # -- Storage type: "pvc" or "hostPath" type: pvc # -- Host path configuration when type is "hostPath" From 2161cdc1d7f1778dac96496f4f3ca119f5a03d9e Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Tue, 28 Apr 2026 10:05:05 -0500 Subject: [PATCH 31/37] Move adminUsers under oidc and require that oidc.enabled be true iff adminUsers is nonempty Co-authored-by: Copilot --- README.md | 18 +++++++++++------- ci/houston2-i2-pelican-cache-values.yaml | 21 ++++++++++----------- ci/uw-osdf-cache-values.yaml | 15 +++++++-------- templates/_helpers.tpl | 7 +++++++ templates/configmap-pelican.yaml | 4 ++-- values.yaml | 8 ++++---- 6 files changed, 41 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 8fc59b9..589fd8a 100644 --- a/README.md +++ b/README.md @@ -141,13 +141,20 @@ Set `issuerKey.existingSecret` to that Secret name. | Parameter | Default | Description | |---|---|---| -| `adminUsers` | `[]` | List of OIDC `sub` claims of users that should have admin access | | `webPassword.existingSecret` | `""` | Existing Secret for web UI password | | `webPassword.key` | `server-web-passwd` | Key within the web password Secret | +| `oidc.enabled` | `false` | Enable OIDC authentication | +| `oidc.existingSecret` | `""` | Secret with `client.id` and `client.secret` keys | +| `oidc.adminUsers` | `[]` | OIDC `sub` claims for UI admins; must be nonempty when `oidc.enabled=true` | You must create a secret containing a file named `server-web-passwd` that was created by running `pelican generate password` and specify that as `webPassword.existingSecret`. +You may also have admins log in via OIDC. In this case, set `oidc.enabled` to `true`, +add a Secret for contacting the Identity Provider with an OIDC client ID and secret +(`client.id` and `client.secret`, respectively), and add a list of `sub` claims to +`oidc.adminUsers`. + ### Federation (customization optional) | Parameter | Default | Description | @@ -233,8 +240,6 @@ Note: When `hostNetwork` is enabled, a Service does not get created, in which ca | `lotman.pvc.existingClaim` | `""` | Existing Lotman PVC name (if set, ignores `storageClass` and `size`) | | `lotman.pvc.storageClass` | `""` | StorageClass for new Lotman PVC (required if `existingClaim` is not set and `enabled=true`) | | `lotman.pvc.size` | `10Gi` | Lotman PVC size (used when creating a new PVC) | -| `oidc.enabled` | `false` | Enable OIDC authentication | -| `oidc.existingSecret` | `""` | Secret with `client.id` and `client.secret` keys | When `sleep` is `true`, the `pelican-cache` container starts with `sleep infinity` for debugging. After you `kubectl exec` into the container, you can start the cache manually with `pelican cache serve`. @@ -354,10 +359,9 @@ lotman: oidc: enabled: true existingSecret: osdf-component-oidc - -adminUsers: - - "http://cilogon.org/serverE/users/12345" - - "http://cilogon.org/serverA/users/67890" + adminUsers: + - "http://cilogon.org/serverE/users/12345" + - "http://cilogon.org/serverA/users/67890" webPassword: existingSecret: my-web-passwd-secret diff --git a/ci/houston2-i2-pelican-cache-values.yaml b/ci/houston2-i2-pelican-cache-values.yaml index 6b3b3ee..0dd5243 100644 --- a/ci/houston2-i2-pelican-cache-values.yaml +++ b/ci/houston2-i2-pelican-cache-values.yaml @@ -69,17 +69,16 @@ logging: oidc: enabled: true existingSecret: nrp-oidc-client-secret - -adminUsers: - - "http://cilogon.org/serverE/users/133679" - - "http://cilogon.org/serverA/users/10832" - - "http://cilogon.org/serverA/users/21441" - - "http://cilogon.org/serverA/users/46022246" - - "http://cilogon.org/serverA/users/9265706" - - "http://cilogon.org/serverE/users/57152" - - "http://cilogon.org/serverE/users/130835" - - "http://cilogon.org/serverE/users/245993" - - "http://cilogon.org/serverB/users/51444962" + adminUsers: + - "http://cilogon.org/serverE/users/133679" + - "http://cilogon.org/serverA/users/10832" + - "http://cilogon.org/serverA/users/21441" + - "http://cilogon.org/serverA/users/46022246" + - "http://cilogon.org/serverA/users/9265706" + - "http://cilogon.org/serverE/users/57152" + - "http://cilogon.org/serverE/users/130835" + - "http://cilogon.org/serverE/users/245993" + - "http://cilogon.org/serverB/users/51444962" webPassword: existingSecret: osdf-web-pass-nrp diff --git a/ci/uw-osdf-cache-values.yaml b/ci/uw-osdf-cache-values.yaml index 5337c0b..92d15c9 100644 --- a/ci/uw-osdf-cache-values.yaml +++ b/ci/uw-osdf-cache-values.yaml @@ -76,14 +76,13 @@ lotman: oidc: enabled: true existingSecret: osdf-component-oidc - -adminUsers: - - "http://cilogon.org/serverE/users/133679" - - "http://cilogon.org/serverA/users/10832" - - "http://cilogon.org/serverA/users/21441" - - "http://cilogon.org/serverA/users/46022246" - - "http://cilogon.org/serverA/users/9265706" - - "http://cilogon.org/serverE/users/57152" + adminUsers: + - "http://cilogon.org/serverE/users/133679" + - "http://cilogon.org/serverA/users/10832" + - "http://cilogon.org/serverA/users/21441" + - "http://cilogon.org/serverA/users/46022246" + - "http://cilogon.org/serverA/users/9265706" + - "http://cilogon.org/serverE/users/57152" webPassword: existingSecret: osdf-prod-pelican-webserver-passwd diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index 2ad16a8..47e7989 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -154,6 +154,13 @@ Validate required values and conditional requirements. {{- if eq (trim (default "" .Values.oidc.existingSecret)) "" }} {{- fail "oidc.existingSecret must be nonempty when oidc.enabled is true" }} {{- end }} + {{- if eq (len .Values.oidc.adminUsers) 0 }} + {{- fail "oidc.adminUsers must be nonempty when oidc.enabled is true" }} + {{- end }} +{{- end }} + +{{- if and (not .Values.oidc.enabled) (gt (len .Values.oidc.adminUsers) 0) }} + {{- fail "oidc.enabled must be true when oidc.adminUsers is nonempty" }} {{- end }} {{- if eq (trim (default "" .Values.webPassword.existingSecret)) "" }} diff --git a/templates/configmap-pelican.yaml b/templates/configmap-pelican.yaml index ae1db74..ecfdcec 100644 --- a/templates/configmap-pelican.yaml +++ b/templates/configmap-pelican.yaml @@ -22,9 +22,9 @@ So we mount it at cert-orig instead (see deployment.yaml). */}} TlsCertificate: "/etc/pelican/cert-orig/tls.crt" TlsKey: "/etc/pelican/cert-orig/tls.key" Hostname: {{ required "serverHostname is required" .Values.serverHostname | quote }} - {{- if .Values.adminUsers }} + {{- if .Values.oidc.adminUsers }} UIAdminUsers: - {{- toYaml .Values.adminUsers | nindent 8 }} + {{- toYaml .Values.oidc.adminUsers | nindent 8 }} {{- end }} {{- if .Values.webPassword.existingSecret }} UIPasswordFile: /etc/pelican/server-web-passwd diff --git a/values.yaml b/values.yaml index 295d8ff..dba5952 100644 --- a/values.yaml +++ b/values.yaml @@ -168,10 +168,10 @@ oidc: enabled: false # -- Name of an existing Secret with keys "client.id" and "client.secret" existingSecret: "" - -# -- List of CILogon admin user identities for the web UI -adminUsers: [] -# - "http://cilogon.org/serverA/users/12345" + # -- List of CILogon admin user identities for the web UI. + # -- Must be nonempty when enabled is true. + adminUsers: [] + # - "http://cilogon.org/serverA/users/12345" webPassword: # -- Required: Name of an existing Secret containing the web UI password file From 81be434eb088d8fdfa424f2cb4fc2fd8997fca54 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Tue, 28 Apr 2026 10:34:45 -0500 Subject: [PATCH 32/37] Reorder the settings in values.yaml to make more sense --- templates/deployment.yaml | 2 +- values.yaml | 104 ++++++++++++++++++-------------------- 2 files changed, 51 insertions(+), 55 deletions(-) diff --git a/templates/deployment.yaml b/templates/deployment.yaml index 297ec3a..a9baea0 100644 --- a/templates/deployment.yaml +++ b/templates/deployment.yaml @@ -5,7 +5,7 @@ metadata: labels: {{- include "pelican-cache.labels" . | nindent 4 }} spec: - replicas: {{ .Values.replicaCount }} + replicas: 1 strategy: type: Recreate selector: diff --git a/values.yaml b/values.yaml index dba5952..e4ca5c2 100644 --- a/values.yaml +++ b/values.yaml @@ -1,21 +1,5 @@ -# -- Override the chart name -nameOverride: "" -# -- Override the full release name -fullnameOverride: "" - -# -- Number of replicas (typically 1 for a cache) -replicaCount: 1 - # -- (Required) External FQDN of the cache server serverHostname: "" - -# -- Server process settings -server: - # -- Cache service port - cachePort: 8443 - # -- Web UI HTTPS port - webPort: 443 - # -- (Required) Site name reported to the federation sitename: "" @@ -24,30 +8,10 @@ federation: # -- Discovery URL for the federation (OSDF default) discoveryUrl: "https://osg-htc.org" -# -- Logrotate sidecar configuration -logrotate: - image: - repository: hub.opensciencegrid.org/opensciencegrid/logrotate - tag: "24-release" - resources: - requests: - cpu: "1" - memory: "500M" - limits: - cpu: "2" - memory: "2G" - size: 500M - rotate: 10 - # -- Cache data storage configuration # -- You must choose between "pvc" and "hostPath" to determine where to # -- persist, and then configure the details in the appropriate subsections. cache: - # -- Cache container image - image: - repository: hub.opensciencegrid.org/pelican_platform/cache - tag: "" - pullPolicy: IfNotPresent # -- Storage type: "pvc" or "hostPath" type: pvc # -- Host path configuration when type is "hostPath" @@ -79,6 +43,12 @@ cache: filesNominalSize: "" # -- diskusage base file tracking size (e.g. "16500g") filesBaseSize: "" + # -- Cache container image + image: + repository: hub.opensciencegrid.org/pelican_platform/cache + tag: "" + pullPolicy: IfNotPresent + # -- Resource request for cache resources: requests: cpu: "1000m" @@ -108,15 +78,6 @@ tls: # -- if this is set, tls.certManager.enabled must be false. existingSecret: "" -# -- Service configuration -service: - type: LoadBalancer - # -- Request a specific load balancer IP - loadBalancerIP: "" - externalTrafficPolicy: Local - # -- Extra annotations (e.g. external-dns, metallb) - annotations: {} - # -- Logging configuration logging: persistence: @@ -146,6 +107,21 @@ logging: # Http: warn # Xrd: warn +# -- Logrotate sidecar configuration +logrotate: + image: + repository: hub.opensciencegrid.org/opensciencegrid/logrotate + tag: "24-release" + resources: + requests: + cpu: "1" + memory: "500M" + limits: + cpu: "2" + memory: "2G" + size: 500M + rotate: 10 + # -- Lotman (lot-based storage management) lotman: # -- Enable lotman for the cache. You must fill out the pvc section below @@ -179,20 +155,33 @@ webPassword: # -- Key within the web password Secret key: "server-web-passwd" -# -- XRootD configuration -xrootd: - # -- Raw xrootd.conf content (creates a ConfigMap and mounts it) - extraConfig: "" - -# -- Debugging: set this to true to sleep instead of starting the cache -sleep: false +# -- Use host networking for the pod +hostNetwork: false # -- Network policy networkPolicy: enabled: true -# -- Use host networking for the pod -hostNetwork: false +# -- Server process settings +server: + # -- Cache service port + cachePort: 8443 + # -- Web UI HTTPS port + webPort: 443 + +# -- Service configuration +service: + type: LoadBalancer + # -- Request a specific load balancer IP + loadBalancerIP: "" + externalTrafficPolicy: Local + # -- Extra annotations (e.g. external-dns, metallb) + annotations: {} + +# -- XRootD configuration +xrootd: + # -- Raw xrootd.conf content (creates a ConfigMap and mounts it) + extraConfig: "" # -- Node selector for pod scheduling nodeSelector: {} @@ -230,3 +219,10 @@ extraVolumeMounts: [] # chart-managed settings so they can override them. extraPelicanConfig: {} +# -- Debugging: set this to true to sleep instead of starting the cache +sleep: false + +# -- Override the chart name +nameOverride: "" +# -- Override the full release name +fullnameOverride: "" From d141a970a46c08eca77d35231a705726ad3f5f82 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Tue, 28 Apr 2026 11:39:50 -0500 Subject: [PATCH 33/37] Split the application settings for cache and logging from the Kubernetes settings The `cache` block now only controls the Kubernetes resources for the cache, and the `logging` and `logrotate` blocks now only control the Kubernetes resources for the logging PVC and logrotate image. Application settings (cache tuning like `highWaterMark`, logging levels, logrotate size and count) have been moved into new `cacheConfig` and `loggingConfig` blocks. Co-authored-by: Copilot --- README.md | 34 +++++++------ ci/houston2-i2-pelican-cache-values.yaml | 10 ++-- ci/itb-osdf-pelican-cache-values.yaml | 8 ++- ci/uw-osdf-cache-values.yaml | 14 +++-- templates/configmap-logrotate.yaml | 4 +- templates/configmap-pelican.yaml | 34 ++++++------- values.yaml | 65 ++++++++++++++---------- 7 files changed, 98 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index 589fd8a..387a6e3 100644 --- a/README.md +++ b/README.md @@ -131,11 +131,13 @@ Set `issuerKey.existingSecret` to that Secret name. | Parameter | Default | Description | |---|---|---| | `logging.persistence.separateVolume` | `true` | Provision a dedicated PVC for `/var/log/pelican`; when `false`, the data volume is also mounted at `/var/log` so `/var/log/pelican` can still live on cache storage. Logrotate always runs. | -| `logging.level` | `INFO` | Global Pelican log level | | `logging.persistence.existingClaim` | `""` | Existing logging PVC name (if set, ignores `storageClass` and `size`) | | `logging.persistence.storageClass` | `""` | StorageClass for new logging PVC (required if `existingClaim` is not set and `separateVolume=true`) | | `logging.persistence.size` | `50Gi` | Logging PVC size (used when creating a new PVC) | -| `logging.cache` | `{}` | Per-subsystem log levels (map, e.g. `{Pss: debug, Pfc: debug}`) | +| `loggingConfig.level` | `INFO` | Global Pelican log level | +| `loggingConfig.cache` | `{}` | Per-subsystem log levels (map, e.g. `{Pss: debug, Pfc: debug}`) | +| `loggingConfig.rotateSize` | `500M` | Log file size threshold that triggers rotation | +| `loggingConfig.rotateCount` | `10` | Number of rotated log files to keep | ### Admin / Web UI (customization required) @@ -181,13 +183,15 @@ not configurable. | Parameter | Default | Description | |---|---|---| -| `cache.blocksToPrefetch` | `""` | Blocks to prefetch (e.g. `10`) | -| `cache.concurrency` | `""` | Max concurrent requests (e.g. `240` — guidance: 10× core count) | -| `cache.highWaterMark` | `""` | Eviction high watermark (e.g. `27000g`) | -| `cache.lowWaterMark` | `""` | Eviction low watermark (e.g. `25000g`) | -| `cache.filesMaxSize` | `""` | XRootD diskusage max tracked size. Must be < low watermark. | -| `cache.filesNominalSize` | `""` | XRootD diskusage nominal tracked size | -| `cache.filesBaseSize` | `""` | XRootD diskusage base tracked size | +| `cacheConfig.blocksToPrefetch` | `""` | Blocks to prefetch (e.g. `10`) | +| `cacheConfig.concurrency` | `""` | Max concurrent requests (e.g. `240` — guidance: 10× core count) | +| `cacheConfig.highWaterMark` | `""` | Eviction high watermark (e.g. `27000g` or `95`) | +| `cacheConfig.lowWaterMark` | `""` | Eviction low watermark (e.g. `25000g` or `90`) | +| `cacheConfig.filesMaxSize` | `""` | XRootD diskusage max tracked size. Must be < low watermark. | +| `cacheConfig.filesNominalSize` | `""` | XRootD diskusage nominal tracked size | +| `cacheConfig.filesBaseSize` | `""` | XRootD diskusage base tracked size | + +`highWaterMark` and `lowWaterMark` may be absolute sizes ending in `g` (gigabytes) or disk space percentages (no suffix). For details on `Files*Size` parameters, see the [XRootD PFC documentation](https://xrootd.web.cern.ch/doc/dev56/pss_config.pdf) (search for "diskusage"). @@ -225,8 +229,6 @@ Note: When `hostNetwork` is enabled, a Service does not get created, in which ca | `logrotate.resources.requests.memory` | `500M` | Logrotate memory request | | `logrotate.resources.limits.cpu` | `2` | Logrotate CPU limit | | `logrotate.resources.limits.memory` | `2G` | Logrotate memory limit | -| `logrotate.size` | `500M` | Log file size threshold that triggers rotation | -| `logrotate.rotate` | `10` | Number of rotated log files to keep | ### Optional Components @@ -317,15 +319,17 @@ cache: type: hostPath hostPath: path: /srv/pelican-cache/ - blocksToPrefetch: 10 - concurrency: 240 - highWaterMark: 27000g - lowWaterMark: 25000g resources: requests: cpu: "24" memory: "48Gi" +cacheConfig: + blocksToPrefetch: 10 + concurrency: 240 + highWaterMark: 27000g + lowWaterMark: 25000g + issuerKey: existingSecret: my-cache-issuer-key diff --git a/ci/houston2-i2-pelican-cache-values.yaml b/ci/houston2-i2-pelican-cache-values.yaml index 0dd5243..eb9a6b8 100644 --- a/ci/houston2-i2-pelican-cache-values.yaml +++ b/ci/houston2-i2-pelican-cache-values.yaml @@ -16,7 +16,6 @@ cache: type: hostPath hostPath: path: /cache - concurrency: 160 resources: requests: cpu: "16000m" @@ -25,6 +24,9 @@ cache: cpu: "16000m" memory: "210Gi" +cacheConfig: + concurrency: 160 + issuerKey: existingSecret: houston2-i2-pelican-pelican-cache-key secretKey: issuer.pem @@ -59,12 +61,14 @@ logrotate: logging: persistence: separateVolume: false + # NOTE: logging.origin and server.webPort from the real deployment are not + # supported as first-class chart values; use extraPelicanConfig if needed. + +loggingConfig: level: INFO cache: Pss: warn Scitokens: warn - # NOTE: logging.origin and server.webPort from the real deployment are not - # supported as first-class chart values; use extraPelicanConfig if needed. oidc: enabled: true diff --git a/ci/itb-osdf-pelican-cache-values.yaml b/ci/itb-osdf-pelican-cache-values.yaml index 28fdbe0..9e5b5fa 100644 --- a/ci/itb-osdf-pelican-cache-values.yaml +++ b/ci/itb-osdf-pelican-cache-values.yaml @@ -18,12 +18,14 @@ cache: pvc: storageClass: 3x-replica-hdd-raddus size: 100Gi - concurrency: 40 resources: requests: cpu: "8" memory: "16Gi" +cacheConfig: + concurrency: 40 + issuerKey: existingSecret: itb-osdf-pelican-cache-issuer-keys secretKey: issuer.pem @@ -44,11 +46,13 @@ service: metallb.universe.tf/address-pool: tiger-vlan5 logging: - level: "debug" persistence: separateVolume: true storageClass: 3x-replica-block size: 10Gi + +loggingConfig: + level: "debug" cache: Scitokens: debug Pss: debug diff --git a/ci/uw-osdf-cache-values.yaml b/ci/uw-osdf-cache-values.yaml index 92d15c9..fa49fec 100644 --- a/ci/uw-osdf-cache-values.yaml +++ b/ci/uw-osdf-cache-values.yaml @@ -14,6 +14,12 @@ cache: type: hostPath hostPath: path: /srv/pelican-cache/ + resources: + requests: + cpu: "24" + memory: "48Gi" + +cacheConfig: blocksToPrefetch: 10 concurrency: 240 highWaterMark: 27000g @@ -21,10 +27,6 @@ cache: filesMaxSize: 17500g filesNominalSize: 17000g filesBaseSize: 16500g - resources: - requests: - cpu: "24" - memory: "48Gi" issuerKey: existingSecret: uw-osdf-cache-issuer-key @@ -57,11 +59,13 @@ logrotate: memory: "2G" logging: - level: "debug" persistence: separateVolume: true storageClass: 3x-replica-hdd-raddus size: 50Gi + +loggingConfig: + level: "debug" cache: Pss: "debug" Pfc: "debug" diff --git a/templates/configmap-logrotate.yaml b/templates/configmap-logrotate.yaml index 5f627f0..9b5f187 100644 --- a/templates/configmap-logrotate.yaml +++ b/templates/configmap-logrotate.yaml @@ -9,8 +9,8 @@ data: /var/log/pelican/*.log { compress copytruncate - size {{ .Values.logrotate.size }} + size {{ .Values.loggingConfig.rotateSize }} missingok notifempty - rotate {{ .Values.logrotate.rotate }} + rotate {{ .Values.loggingConfig.rotateCount }} } diff --git a/templates/configmap-pelican.yaml b/templates/configmap-pelican.yaml index ecfdcec..2b9a1cd 100644 --- a/templates/configmap-pelican.yaml +++ b/templates/configmap-pelican.yaml @@ -30,8 +30,8 @@ So we mount it at cert-orig instead (see deployment.yaml). */}} UIPasswordFile: /etc/pelican/server-web-passwd {{- end }} - {{- if .Values.cache.blocksToPrefetch }} - Cache.BlocksToPrefetch: {{ .Values.cache.blocksToPrefetch }} + {{- if .Values.cacheConfig.blocksToPrefetch }} + Cache.BlocksToPrefetch: {{ .Values.cacheConfig.blocksToPrefetch }} {{- end }} {{- if .Values.oidc.enabled }} Cache.EnableOIDC: true @@ -39,23 +39,23 @@ So we mount it at cert-orig instead (see deployment.yaml). */}} {{- if .Values.lotman.enabled }} Cache.EnableLotman: true {{- end }} - {{- if .Values.cache.highWaterMark }} - Cache.HighWaterMark: {{ .Values.cache.highWaterMark }} + {{- if .Values.cacheConfig.highWaterMark }} + Cache.HighWaterMark: {{ .Values.cacheConfig.highWaterMark }} {{- end }} - {{- if .Values.cache.lowWaterMark }} - Cache.LowWaterMark: {{ .Values.cache.lowWaterMark }} + {{- if .Values.cacheConfig.lowWaterMark }} + Cache.LowWaterMark: {{ .Values.cacheConfig.lowWaterMark }} {{- end }} - {{- if .Values.cache.filesMaxSize }} - Cache.FilesMaxSize: {{ .Values.cache.filesMaxSize }} + {{- if .Values.cacheConfig.filesMaxSize }} + Cache.FilesMaxSize: {{ .Values.cacheConfig.filesMaxSize }} {{- end }} - {{- if .Values.cache.filesNominalSize }} - Cache.FilesNominalSize: {{ .Values.cache.filesNominalSize }} + {{- if .Values.cacheConfig.filesNominalSize }} + Cache.FilesNominalSize: {{ .Values.cacheConfig.filesNominalSize }} {{- end }} - {{- if .Values.cache.filesBaseSize }} - Cache.FilesBaseSize: {{ .Values.cache.filesBaseSize }} + {{- if .Values.cacheConfig.filesBaseSize }} + Cache.FilesBaseSize: {{ .Values.cacheConfig.filesBaseSize }} {{- end }} - {{- if .Values.cache.concurrency }} - Cache.Concurrency: {{ .Values.cache.concurrency }} + {{- if .Values.cacheConfig.concurrency }} + Cache.Concurrency: {{ .Values.cacheConfig.concurrency }} {{- end }} {{- if .Values.oidc.enabled }} @@ -67,10 +67,10 @@ So we mount it at cert-orig instead (see deployment.yaml). */}} Logging: LogLocation: "/var/log/pelican/pelican.log" - Level: {{ .Values.logging.level | quote }} - {{- if .Values.logging.cache }} + Level: {{ .Values.loggingConfig.level | quote }} + {{- if .Values.loggingConfig.cache }} Cache: - {{- toYaml .Values.logging.cache | nindent 8 }} + {{- toYaml .Values.loggingConfig.cache | nindent 8 }} {{- end }} Topology.DisableCacheX509: true diff --git a/values.yaml b/values.yaml index e4ca5c2..fd24dd6 100644 --- a/values.yaml +++ b/values.yaml @@ -8,7 +8,7 @@ federation: # -- Discovery URL for the federation (OSDF default) discoveryUrl: "https://osg-htc.org" -# -- Cache data storage configuration +# -- Cache data storage and deployment configuration # -- You must choose between "pvc" and "hostPath" to determine where to # -- persist, and then configure the details in the appropriate subsections. cache: @@ -29,20 +29,6 @@ cache: # -- PVC size when creating a new PVC; this must be specified # -- unless you are using existingClaim. size: 1000Gi - # -- Number of blocks to prefetch (e.g. 10) - blocksToPrefetch: "" - # -- Maximum concurrent requests (e.g. 240) - concurrency: "" - # -- High water mark for cache eviction (e.g. "27000g") - highWaterMark: "" - # -- Low water mark for cache eviction (e.g. "25000g") - lowWaterMark: "" - # -- diskusage max file tracking size (e.g. "17500g") - filesMaxSize: "" - # -- diskusage nominal file tracking size (e.g. "17000g") - filesNominalSize: "" - # -- diskusage base file tracking size (e.g. "16500g") - filesBaseSize: "" # -- Cache container image image: repository: hub.opensciencegrid.org/pelican_platform/cache @@ -55,6 +41,26 @@ cache: memory: "16Gi" limits: {} +# -- Pelican cache runtime tuning parameters. +# -- These control application-level behavior (eviction, prefetch, concurrency). +# -- All values default to empty string, which omits the setting from the config +# -- and lets Pelican use its built-in defaults. +cacheConfig: + # -- Number of blocks to prefetch (e.g. 10) + blocksToPrefetch: "" + # -- Maximum concurrent requests (e.g. 240; guidance: 10x core count) + concurrency: "" + # -- High water mark for cache eviction (e.g. "27000g" or "95") + highWaterMark: "" + # -- Low water mark for cache eviction (e.g. "25000g" or "90") + lowWaterMark: "" + # -- diskusage max file tracking size (e.g. "17500g"); if specified, must be < lowWaterMark + filesMaxSize: "" + # -- diskusage nominal file tracking size (e.g. "17000g") + filesNominalSize: "" + # -- diskusage base file tracking size (e.g. "16500g") + filesBaseSize: "" + # -- Issuer key configuration issuerKey: # -- Required: Name of an existing Secret containing the issuer key @@ -96,16 +102,6 @@ logging: # -- Logging PVC size when creating a new PVC; this must be specified # -- unless you are using existingClaim. size: 50Gi - # -- Global log level - level: INFO - # -- Per-subsystem cache log levels - cache: {} - # Pss: debug - # Pfc: debug - # PssSetOpt: debug - # Scitokens: warn - # Http: warn - # Xrd: warn # -- Logrotate sidecar configuration logrotate: @@ -119,8 +115,23 @@ logrotate: limits: cpu: "2" memory: "2G" - size: 500M - rotate: 10 + +# -- Pelican logging and logrotate runtime settings. +loggingConfig: + # -- Global Pelican log level + level: INFO + # -- Per-subsystem cache log levels + cache: {} + # Pss: debug + # Pfc: debug + # PssSetOpt: debug + # Scitokens: warn + # Http: warn + # Xrd: warn + # -- Log file size threshold that triggers rotation + rotateSize: 500M + # -- Number of rotated log files to keep + rotateCount: 10 # -- Lotman (lot-based storage management) lotman: From 2c570ac24939606f1576af9ce5febf91e6a875b6 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Tue, 28 Apr 2026 17:14:25 -0500 Subject: [PATCH 34/37] CI: specify additional required settings in the "minimal" test --- .github/workflows/ci.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 962de0c..9498012 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -31,7 +31,13 @@ jobs: matrix: include: - name: minimal - args: "--set serverHostname=test.example.com" + args: >- + --set serverHostname=test.example.com + --set sitename=TEST_EXAMPLE_COM_CACHE + --set cache.pvc.storageClass=fast-nvme + --set logging.persistence.storageClass=standard + --set issuerKey.existingSecret=my-issuer-key-secret + --set webPassword.existingSecret=my-web-passwd-secret - name: uw-osdf-cache args: "-f ci/uw-osdf-cache-values.yaml" - name: itb-osdf-pelican-cache From f42eac58dd24ec5081b6653a36b8bf01010b8ebe Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Thu, 18 Jun 2026 11:47:28 -0500 Subject: [PATCH 35/37] Potential fix for pull request finding Issue: > pelican-cache.sanitizedFederationUrl redeclares $stripped with := twice, which causes a template render error in Helm/Go templates. Use = on the second assignment to update the existing variable instead of redeclaring it. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- templates/_helpers.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index 47e7989..fff99b0 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -61,7 +61,7 @@ This helper: {{- define "pelican-cache.sanitizedFederationUrl" -}} {{- $url := .Values.federation.discoveryUrl }} {{- $stripped := regexReplaceAll "^[^:]+://" $url "" -}} -{{- $stripped := regexReplaceAll "[^A-Za-z0-9._-]" $stripped "_" -}} +{{- $stripped = regexReplaceAll "[^A-Za-z0-9._-]" $stripped "_" -}} {{- $stripped }} {{- end }} From b40e349af114befd4b6ceb08a42d302f38c2287f Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Thu, 18 Jun 2026 12:05:52 -0500 Subject: [PATCH 36/37] Require issuerKey.secretKey There is a default value, but error out if the user has explicilty set it to an empty string. --- templates/deployment.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/deployment.yaml b/templates/deployment.yaml index a9baea0..8a1de56 100644 --- a/templates/deployment.yaml +++ b/templates/deployment.yaml @@ -84,7 +84,7 @@ spec: - name: issuer-key readOnly: true mountPath: /etc/pelican/issuer-keys/issuer.pem - subPath: {{ .Values.issuerKey.secretKey }} + subPath: {{ required "issuerKey.secretKey is required" .Values.issuerKey.secretKey }} - name: tls-cert {{/* We cannot mount the host cert secret at /etc/pelican/certificates because that would make that directory read-only and Pelican needs to write @@ -190,7 +190,7 @@ its own CA there. So we use a different directory here and in the Pelican confi secret: secretName: {{ required "issuerKey.existingSecret is required" .Values.issuerKey.existingSecret }} items: - - key: {{ .Values.issuerKey.secretKey }} + - key: {{ required "issuerKey.secretKey is required" .Values.issuerKey.secretKey }} path: {{ .Values.issuerKey.secretKey }} mode: 0600 From 8e53d3c9536b7c3b6a55aea7b9042fce59331aee Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Thu, 18 Jun 2026 12:06:54 -0500 Subject: [PATCH 37/37] Delete outdated comment --- ci/houston2-i2-pelican-cache-values.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/ci/houston2-i2-pelican-cache-values.yaml b/ci/houston2-i2-pelican-cache-values.yaml index eb9a6b8..92abd9c 100644 --- a/ci/houston2-i2-pelican-cache-values.yaml +++ b/ci/houston2-i2-pelican-cache-values.yaml @@ -108,9 +108,6 @@ securityContext: capabilities: add: ["SYS_PTRACE"] -# NOTE: XRD_CURLDISABLEX509 is already set unconditionally by the chart's -# deployment template, so it does not need to be repeated in extraEnv. - # Convert remaining env-var config that doesn't have first-class values.yaml # knobs into proper YAML config. extraPelicanConfig: