diff --git a/apps/charts/cups/Chart.yaml b/apps/charts/cups/Chart.yaml
new file mode 100644
index 0000000..4110aa9
--- /dev/null
+++ b/apps/charts/cups/Chart.yaml
@@ -0,0 +1,7 @@
+apiVersion: v2
+version: 1.0.0
+name: cups
+dependencies:
+ - name: common
+ version: 1.0.0
+ repository: file://../../common
diff --git a/apps/charts/cups/templates/deployment.yaml b/apps/charts/cups/templates/deployment.yaml
new file mode 100644
index 0000000..4508e33
--- /dev/null
+++ b/apps/charts/cups/templates/deployment.yaml
@@ -0,0 +1 @@
+{{ include "common.deployment" . }}
diff --git a/apps/charts/cups/templates/pvc.yaml b/apps/charts/cups/templates/pvc.yaml
new file mode 100644
index 0000000..379bad9
--- /dev/null
+++ b/apps/charts/cups/templates/pvc.yaml
@@ -0,0 +1 @@
+{{ include "common.pvc" . }}
diff --git a/apps/charts/cups/templates/secret-external-secrets.yaml b/apps/charts/cups/templates/secret-external-secrets.yaml
new file mode 100644
index 0000000..de340c4
--- /dev/null
+++ b/apps/charts/cups/templates/secret-external-secrets.yaml
@@ -0,0 +1 @@
+{{ include "common.externalSecrets.externalSecrets" . }}
diff --git a/apps/charts/cups/templates/secret-password-generators.yaml b/apps/charts/cups/templates/secret-password-generators.yaml
new file mode 100644
index 0000000..2183e0a
--- /dev/null
+++ b/apps/charts/cups/templates/secret-password-generators.yaml
@@ -0,0 +1 @@
+{{ include "common.externalSecrets.passwordGenerators" . }}
diff --git a/apps/charts/cups/templates/service.yaml b/apps/charts/cups/templates/service.yaml
new file mode 100644
index 0000000..f024c64
--- /dev/null
+++ b/apps/charts/cups/templates/service.yaml
@@ -0,0 +1 @@
+{{ include "common.service" . }}
diff --git a/apps/charts/cups/templates/virtual-service.yaml b/apps/charts/cups/templates/virtual-service.yaml
new file mode 100644
index 0000000..201036c
--- /dev/null
+++ b/apps/charts/cups/templates/virtual-service.yaml
@@ -0,0 +1,53 @@
+{{- if and .Values.virtualService.enabled .Values.subdomain (hasKey .Values.globals "domain") (ne .Values.globals.domain "") }}
+{{- if and .Values.virtualService.gateways.public (hasKey .Values.globals "istio") (hasKey .Values.globals.istio "gateways") (hasKey .Values.globals.istio.gateways "public") (ne .Values.globals.istio.gateways.public "") }}
+apiVersion: networking.istio.io/v1
+kind: VirtualService
+metadata:
+ name: {{ include "common.fullname" . }}-public
+ namespace: {{ .Release.Namespace }}
+ labels:
+ {{- include "common.labels" . | nindent 4 }}
+spec:
+ gateways:
+ - {{ .Values.globals.istio.gateways.public | quote }}
+ - mesh
+ hosts:
+ - {{ include "common.domain" . }}
+ - mesh
+ http:
+ - match:
+ - uri:
+ prefix: "/"
+ route:
+ - destination:
+ host: {{ include "common.fullname" . }}
+ port:
+ number: {{ .Values.virtualService.servicePort | default 631 }}
+---
+{{- end }}
+{{- if and .Values.virtualService.gateways.private (hasKey .Values.globals "istio") (hasKey .Values.globals.istio "gateways") (hasKey .Values.globals.istio.gateways "private") (ne .Values.globals.istio.gateways.private "") }}
+apiVersion: networking.istio.io/v1
+kind: VirtualService
+metadata:
+ name: {{ include "common.fullname" . }}-private
+ namespace: {{ .Release.Namespace }}
+ labels:
+ {{- include "common.labels" . | nindent 4 }}
+spec:
+ gateways:
+ - {{ .Values.globals.istio.gateways.private | quote }}
+ - mesh
+ hosts:
+ - {{ include "common.domain" . }}
+ - mesh
+ http:
+ - match:
+ - uri:
+ prefix: "/"
+ route:
+ - destination:
+ host: {{ include "common.fullname" . }}
+ port:
+ number: {{ .Values.virtualService.servicePort | default 631 }}
+{{- end }}
+{{- end }}
diff --git a/apps/charts/cups/values.yaml b/apps/charts/cups/values.yaml
new file mode 100644
index 0000000..baeb151
--- /dev/null
+++ b/apps/charts/cups/values.yaml
@@ -0,0 +1,218 @@
+image:
+ repository: olbat/cupsd
+ tag: latest
+ pullPolicy: IfNotPresent
+
+# Command to initialize and start CUPS
+command:
+ - /bin/sh
+ - -c
+args:
+ - |
+ echo "Starting CUPS initialization..."
+ mkdir -p /etc/cups/ssl /var/spool/cups/tmp /var/log/cups /var/cache/cups /var/run
+ chmod 755 /etc/cups /var/spool/cups 2>&1 || true
+
+ if [ ! -f /etc/cups/cupsd.conf ]; then
+ echo "CUPS config not found, creating configuration with web access..."
+ {
+ echo "Listen *:631"
+ echo "ServerRoot /etc/cups"
+ echo "StateDir /var/spool/cups"
+ echo "CacheDir /var/cache/cups"
+ echo "DataDir /usr/share/cups"
+ echo "AccessLog /var/log/cups/access_log"
+ echo "ErrorLog /var/log/cups/error_log"
+ echo "LogLevel warn"
+ echo "MaxLogSize 0"
+ echo "SystemGroup lpadmin"
+ echo "User root"
+ echo "Group lp"
+ echo "ServerAdmin root"
+ echo ""
+ echo "# Network printer discovery"
+ echo "BrowseLocalProtocols dnssd"
+ echo "BrowseRemoteProtocols dnssd"
+ echo "BrowseAddress @LOCAL"
+ echo "BrowsePoll 192.168.0.0/16"
+ echo "BrowsePoll 192.168.1.0/24"
+ echo "BrowsePoll 192.168.10.0/24"
+ echo "BrowsePoll 192.168.20.0/24"
+ echo "BrowsePoll 192.168.30.0/24"
+ echo ""
+ echo ""
+ echo " Order allow,deny"
+ echo " Allow all"
+ echo ""
+ echo ""
+ echo ""
+ echo " Order allow,deny"
+ echo " Allow all"
+ echo ""
+ echo ""
+ echo ""
+ echo " AuthType Default"
+ echo " Require user @SYSTEM"
+ echo " Order allow,deny"
+ echo " Allow all"
+ echo ""
+ echo ""
+ echo ""
+ echo " JobPrivateAccess default"
+ echo " JobPrivateValues default"
+ echo " SubscriptionPrivateAccess default"
+ echo " SubscriptionPrivateValues default"
+ echo " "
+ echo " Require user @OWNER @SYSTEM"
+ echo " "
+ echo " "
+ echo " Require user @OWNER @SYSTEM"
+ echo " "
+ echo " "
+ echo " Require user @SYSTEM"
+ echo " "
+ echo " "
+ echo " Require user @SYSTEM"
+ echo " "
+ echo " "
+ echo " Order deny,allow"
+ echo " "
+ echo ""
+ } > /etc/cups/cupsd.conf
+ echo "Created CUPS configuration file"
+ else
+ echo "CUPS config already exists, ensuring access controls and discovery are present..."
+ # Add network discovery settings if not present
+ if ! grep -q "BrowseLocalProtocols" /etc/cups/cupsd.conf; then
+ {
+ echo ""
+ echo "# Network printer discovery"
+ echo "BrowseLocalProtocols dnssd"
+ echo "BrowseRemoteProtocols dnssd"
+ echo "BrowseAddress @LOCAL"
+ echo "BrowsePoll 192.168.0.0/16"
+ echo "BrowsePoll 192.168.1.0/24"
+ echo "BrowsePoll 192.168.10.0/24"
+ echo "BrowsePoll 192.168.20.0/24"
+ echo "BrowsePoll 192.168.30.0/24"
+ } >> /etc/cups/cupsd.conf
+ echo "Added network discovery settings"
+ fi
+ # Always ensure Location sections exist for web access
+ if ! grep -q "" /etc/cups/cupsd.conf; then
+ {
+ echo ""
+ echo ""
+ echo " Order allow,deny"
+ echo " Allow all"
+ echo ""
+ echo ""
+ echo ""
+ echo " Order allow,deny"
+ echo " Allow all"
+ echo ""
+ echo ""
+ echo ""
+ echo " AuthType Default"
+ echo " Require user @SYSTEM"
+ echo " Order allow,deny"
+ echo " Allow all"
+ echo ""
+ } >> /etc/cups/cupsd.conf
+ echo "Added access control sections to existing config"
+ fi
+ fi
+
+ echo "Testing CUPS configuration..."
+ /usr/sbin/cupsd -t 2>&1 || echo "Config test warnings (may be normal)"
+
+ echo "Starting CUPS daemon in foreground..."
+ exec /usr/sbin/cupsd -f
+
+subdomain: cups
+
+# Deployment configuration
+deployment:
+ strategy: Recreate
+ replicas: 1
+ revisionHistoryLimit: 0
+ hostNetwork: true # Required for printer discovery and Android device access
+ dnsPolicy: ClusterFirstWithHostNet
+
+# Container configuration
+container:
+ ports:
+ - name: ipp
+ port: 631
+ protocol: TCP
+ healthProbe:
+ type: tcpSocket
+ port: ipp
+ securityContext:
+ privileged: false
+ runAsUser: 0 # CUPS typically needs root for printer access
+
+# Service configuration
+service:
+ ports:
+ - name: ipp
+ port: 631
+ targetPort: 631
+ protocol: TCP
+ type: ClusterIP
+ # Note: With hostNetwork, the service is mainly for service discovery
+ # CUPS will be accessible directly on node IP:631
+
+# Volume configuration
+volumes:
+ - name: config
+ mountPath: /etc/cups
+ persistentVolumeClaim: cups-config
+ - name: spool
+ mountPath: /var/spool/cups
+ persistentVolumeClaim: cups-spool
+
+# Persistent volume claims
+persistentVolumeClaims:
+ - name: config
+ size: 1Gi
+ - name: spool
+ size: 5Gi
+
+# VirtualService configuration (for web UI)
+# Enables access via https://cups.{domain} for administration
+# IPP printing can still work via direct node IP or through the service
+virtualService:
+ enabled: true
+ gateways:
+ public: true
+ private: true
+ servicePort: 631
+
+# OIDC client configuration (disabled with hostNetwork)
+# Web UI will be accessible directly via node IP
+oidc:
+ enabled: false
+
+# External Secrets configuration
+externalSecrets:
+ - name: "{release}-secrets"
+ passwords:
+ - name: cupspassword
+ length: 16
+ allowRepeat: true
+ encoding: hex
+ secretKeys:
+ - cupspassword
+
+# Environment variables
+env:
+ TZ:
+ value: "{timezone}"
+ CUPSADMIN:
+ value: admin
+ CUPSPASSWORD:
+ valueFrom:
+ secretKeyRef:
+ name: "{release}-secrets"
+ key: cupspassword
diff --git a/apps/common/README.md b/apps/common/README.md
index da34f47..f1bbdc8 100644
--- a/apps/common/README.md
+++ b/apps/common/README.md
@@ -107,6 +107,18 @@ container:
runAsUser: 1000
runAsGroup: 1000
+# Command and args (optional)
+# Override the container's default command/entrypoint
+# Useful for initialization scripts or custom startup logic
+command:
+ - /bin/sh
+ - -c
+args:
+ - |
+ echo "Running initialization..."
+ # Your custom startup logic here
+ exec /app/start.sh
+
# Service configuration
service:
# Single service (simple case)
@@ -447,7 +459,7 @@ env:
The library provides full resource templates that can be included directly:
-- `common.deployment` - Full Deployment resource with all standard configurations
+- `common.deployment` - Full Deployment resource with all standard configurations (supports custom command/args)
- `common.service` - Full Service resource(s) - supports multiple services
- `common.pvc` - Full PVC resources - supports multiple PVCs
- `common.virtualService` - Full VirtualService resources (public + private gateways)
diff --git a/apps/common/templates/_helpers.tpl b/apps/common/templates/_helpers.tpl
index d517a97..17a6bda 100644
--- a/apps/common/templates/_helpers.tpl
+++ b/apps/common/templates/_helpers.tpl
@@ -312,6 +312,12 @@ spec:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy | default "IfNotPresent" }}
+ {{- if .Values.command }}
+ command: {{- toYaml .Values.command | nindent 12 }}
+ {{- end }}
+ {{- if .Values.args }}
+ args: {{- toYaml .Values.args | nindent 12 }}
+ {{- end }}
ports:
{{ include "common.containerPorts" . | indent 12 }}
{{- if .Values.container.healthProbe }}