Author SHA1 Message Date
f.weber f5b19464ba chore: bump chart appVersion to 1.6.3-2 [skip ci] 2026-08-13 13:50:19 +00:00
f.weber 09400b7882 Update Dockerfile to include gpgv for enhanced security during package installation
CI / lint (push) Failing after 3s
CI / smoke-test (push) Failing after 13s
Release image / release (push) Successful in 22m40s
Release chart / release (release) Has been skipped
2026-08-13 15:33:28 +02:00
f.weber f345f469da Add ArtifactHub repository metadata and improve Helm chart configuration
CI / lint (push) Failing after 3s
Release chart / release (release) Successful in 9s
CI / smoke-test (push) Failing after 5s
- Introduced artifacthub-repo.yml for ownership verification on ArtifactHub.
- Updated release-chart.yaml to publish the ArtifactHub metadata as an OCI artifact.
- Bumped chart version to 0.2.2 and added relevant changes to Chart.yaml.
- Enhanced job-reconcile.yaml by adding a writable /tmp directory for the reconcile job.
2026-08-13 12:42:41 +02:00
f.weber a08ed898e8 Update Helm chart version and modify probes to use exec for readiness and liveness checks
CI / lint (push) Failing after 3s
CI / smoke-test (push) Failing after 5s
Release chart / release (release) Successful in 6s
2026-08-13 12:30:17 +02:00
f.weber 1675ea9a4e Enhance Helm chart with ServiceAccount support and configurable ports
CI / lint (push) Failing after 2s
CI / smoke-test (push) Failing after 9s
Release chart / release (release) Successful in 12s
- Updated Chart.yaml to version 0.2.0 and added annotations for changes.
- Modified release-chart.yaml to trigger releases via Gitea and handle pre-releases.
- Introduced ServiceAccount configuration in values.yaml and related templates.
- Adjusted internal container ports to prevent conflicts between nginx and aptly.
- Updated README.md and NOTES.txt to reflect new configurations and usage instructions.
2026-08-13 12:12:30 +02:00
13 changed files with 177 additions and 33 deletions
+50 -13
View File
@@ -1,23 +1,37 @@
name: Release chart name: Release chart
# Triggered by `git tag chart/vX.Y.Z && git push --tags`, independently of # Triggered by publishing a Gitea Release whose tag matches `chart/vX.Y.Z`
# image releases — see docs/versioning.md. Never bumps the image: Chart.yaml's # (create the tag + release together via the Gitea UI's "New Release" page,
# committed appVersion (last set by release-image.yaml) is what gets packaged, # or `git tag chart/vX.Y.Z && git push --tags` followed by turning that tag
# so a chart-only release always pins the last released image, never `latest`. # into a Release) — independently of image releases (release-image.yaml), see
# docs/versioning.md.
#
# Ticking "This is a pre-release" on that Gitea Release bakes
# `artifacthub.io/prerelease: "true"` into the packaged Chart.yaml (never
# committed back — see the "Set ArtifactHub prerelease annotation" step
# below), so ArtifactHub lists that specific chart version as a pre-release.
#
# This job also matches the `release: published` event fired by
# release-image.yaml's own auto-created Gitea Release for `image/v*` tags —
# the `if:` guard below skips anything whose tag isn't `chart/v*`.
# #
# Chart version is pure SemVer, deliberately WITHOUT the `+up<aptly>` build # Chart version is pure SemVer, deliberately WITHOUT the `+up<aptly>` build
# metadata bookstack-chart uses: Helm rewrites `+` to `_` on OCI push (and # metadata bookstack-chart uses: Helm rewrites `+` to `_` on OCI push (and
# back on pull), which breaks listing in some third-party tooling (e.g. # back on pull), which breaks listing in some third-party tooling (e.g.
# Rancher). The aptly version lives in appVersion instead. # Rancher). The aptly version lives in appVersion instead. A SemVer
# pre-release suffix (`chart/v0.2.0-rc.1`) works fine and is unaffected.
# #
# The chart is pushed under f.weber/charts/aptly, NOT f.weber/aptly: Gitea's # The chart is pushed under f.weber/charts/aptly, NOT f.weber/aptly: Gitea's
# package registry stores both container images and Helm OCI charts as # package registry stores both container images and Helm OCI charts as
# generic OCI artifacts, and a chart sharing the exact repository path with # generic OCI artifacts, and a chart sharing the exact repository path with
# the container image of the same name makes the package listing/type # the container image of the same name makes the package listing/type
# ambiguous. A distinct `charts/` path keeps the two package kinds apart. # ambiguous. A distinct `charts/` path keeps the two package kinds apart.
#
# Also pushes charts/aptly/artifacthub-repo.yml as a companion OCI artifact
# (tag `artifacthub.io`) on every run, proving ownership of this repository
# to ArtifactHub — see the "Publish ArtifactHub repository metadata" step.
on: on:
push: release:
tags: types: [published]
- 'chart/v*'
concurrency: concurrency:
group: release-chart group: release-chart
@@ -25,15 +39,16 @@ concurrency:
jobs: jobs:
release: release:
if: startsWith(gitea.event.release.tag_name, 'chart/v')
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Parse chart version from tag - name: Parse chart version from the release's tag
id: version id: version
run: echo "version=${GITEA_REF_NAME#chart/v}" >> "$GITEA_OUTPUT" run: echo "version=${TAG_NAME#chart/v}" >> "$GITEA_OUTPUT"
env: env:
GITEA_REF_NAME: ${{ gitea.ref_name }} TAG_NAME: ${{ gitea.event.release.tag_name }}
- uses: azure/setup-helm@v4.3.0 - uses: azure/setup-helm@v4.3.0
with: with:
@@ -54,6 +69,11 @@ jobs:
exit 1 exit 1
fi fi
- name: Set ArtifactHub prerelease annotation (packaged copy only, never committed)
if: gitea.event.release.prerelease
run: |
yq -i '.annotations["artifacthub.io/prerelease"] = "true"' charts/aptly/Chart.yaml
- name: Import GPG signing key - name: Import GPG signing key
uses: crazy-max/ghaction-import-gpg@v6 uses: crazy-max/ghaction-import-gpg@v6
with: with:
@@ -88,11 +108,28 @@ jobs:
--username "${{ secrets.REGISTRY_USER }}" --password-stdin --username "${{ secrets.REGISTRY_USER }}" --password-stdin
helm push "aptly-${{ steps.version.outputs.version }}.tgz" oci://git.morlana.online/f.weber/charts helm push "aptly-${{ steps.version.outputs.version }}.tgz" oci://git.morlana.online/f.weber/charts
- name: Create Gitea release with chart artifacts # Publishes charts/aptly/artifacthub-repo.yml as its own OCI artifact
# (tag `artifacthub.io`) next to the chart — this is how ArtifactHub
# verifies ownership of an OCI-based Helm repository; there's no
# index.yaml it could otherwise crawl a metadata file out of. Uses the
# same registry credentials as the chart push above, nothing
# ArtifactHub-specific is needed (ArtifactHub has no publish API to
# authenticate against — it only reads the registry).
- uses: oras-project/setup-oras@v1
- name: Publish ArtifactHub repository metadata (ownership verification)
run: |
echo "${{ secrets.REGISTRY_TOKEN }}" | oras login git.morlana.online \
--username "${{ secrets.REGISTRY_USER }}" --password-stdin
oras push git.morlana.online/f.weber/charts/aptly:artifacthub.io \
--config /dev/null:application/vnd.cncf.artifacthub.config.v1+yaml \
charts/aptly/artifacthub-repo.yml:application/vnd.cncf.artifacthub.repository-metadata.layer.v1.yaml
- name: Attach chart artifacts to the Gitea release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
with: with:
tag_name: ${{ gitea.ref_name }} tag_name: ${{ gitea.event.release.tag_name }}
name: "aptly chart ${{ steps.version.outputs.version }}" name: "aptly chart ${{ steps.version.outputs.version }}"
prerelease: ${{ gitea.event.release.prerelease }}
body: | body: |
`helm pull oci://git.morlana.online/f.weber/charts/aptly --version ${{ steps.version.outputs.version }}` `helm pull oci://git.morlana.online/f.weber/charts/aptly --version ${{ steps.version.outputs.version }}`
files: | files: |
+8 -3
View File
@@ -5,8 +5,8 @@ description: >-
an nginx read/auth sidecar, a fully aptly-native values API, and declarative an nginx read/auth sidecar, a fully aptly-native values API, and declarative
repo/mirror/publish state reconciled via a Helm hook. repo/mirror/publish state reconciled via a Helm hook.
type: application type: application
version: 0.1.0 version: 0.2.2
appVersion: "1.6.3-1" appVersion: "1.6.3-2"
home: https://git.morlana.online/f.weber/aptly-containerized home: https://git.morlana.online/f.weber/aptly-containerized
sources: sources:
- https://git.morlana.online/f.weber/aptly-containerized - https://git.morlana.online/f.weber/aptly-containerized
@@ -20,7 +20,7 @@ keywords:
maintainers: maintainers:
- name: Florian Weber - name: Florian Weber
email: f.weber@flweber.me email: f.weber@flweber.me
icon: https://www.aptly.info/img/aptly_medium.png icon: https://www.aptly.info/img/logo.png
annotations: annotations:
artifacthub.io/license: MIT artifacthub.io/license: MIT
artifacthub.io/links: | artifacthub.io/links: |
@@ -31,3 +31,8 @@ annotations:
artifacthub.io/signKey: | artifacthub.io/signKey: |
fingerprint: FC35C0FAA26605C4C21C7BBFBF43884145E5AA94 fingerprint: FC35C0FAA26605C4C21C7BBFBF43884145E5AA94
url: https://git.morlana.online/f.weber/aptly-containerized/raw/branch/main/pubkeys/chart-signing.asc url: https://git.morlana.online/f.weber/aptly-containerized/raw/branch/main/pubkeys/chart-signing.asc
artifacthub.io/changes: |
- kind: added
description: Repository ownership verification for ArtifactHub (charts/aptly/artifacthub-repo.yml), published as a companion OCI artifact by release-chart.yaml.
- kind: fixed
description: The reconcile Job's container had readOnlyRootFilesystem but no writable /tmp, so its own API client's scratch files failed to write ("Read-only file system") and every reconcile call died with a misleading "connection" error. A /tmp emptyDir is now mounted there too, matching the aptly/nginx containers in the StatefulSet.
+13 -1
View File
@@ -311,6 +311,15 @@ plain, supported operation.
| `persistence.size` | `20Gi` | immutable once installed unless using `existingClaim` | | `persistence.size` | `20Gi` | immutable once installed unless using `existingClaim` |
| `persistence.annotations` | `{}` | | | `persistence.annotations` | `{}` | |
### ServiceAccount
| Key | Default | Description |
|---|---|---|
| `serviceAccount.create` | `true` | creates a dedicated `ServiceAccount` for this release |
| `serviceAccount.name` | `""` | `create: true` → defaults to the release's fullname; `create: false` → set this to bind an existing `ServiceAccount`, or leave `""` to use the namespace's `default` one |
| `serviceAccount.annotations` | `{}` | e.g. for IRSA/Workload Identity |
| `serviceAccount.automountServiceAccountToken` | `true` | |
### Workload ### Workload
| Key | Default | Description | | Key | Default | Description |
@@ -330,8 +339,11 @@ plain, supported operation.
| Key | Default | Description | | Key | Default | Description |
|---|---|---| |---|---|---|
| `service.type` | `ClusterIP` | | | `service.type` | `ClusterIP` | |
| `service.port` | `8080` | | | `service.port` | `8080` | external port — unaffected by `ports.*` below, which are internal-only |
| `service.annotations` | `{}` | | | `service.annotations` | `{}` | |
| `ports.aptly` | `8080` | container port aptly itself listens on (loopback-only unless `proxy.enabled: false`) |
| `ports.nginx` | `8081` | container port nginx listens on for repo + API traffic; must differ from `ports.aptly` — they're two containers sharing one pod network namespace, and a clash makes nginx fail to start with "address already in use" |
| `ports.metrics` | `9090` | container port nginx listens on for the `/api/metrics` passthrough, when `metrics.service.enabled` |
| `ingress.enabled` | `false` | | | `ingress.enabled` | `false` | |
| `ingress.mode` | `single` | `single` \| `split` — see [docs/security.md](https://git.morlana.online/f.weber/aptly-containerized/src/branch/main/docs/security.md) | | `ingress.mode` | `single` | `single` \| `split` — see [docs/security.md](https://git.morlana.online/f.weber/aptly-containerized/src/branch/main/docs/security.md) |
| `ingress.className` | `""` | | | `ingress.className` | `""` | |
+6
View File
@@ -0,0 +1,6 @@
repositoryID: "1ed468a7-7886-48ad-8835-369bc8a06bc5"
owners:
- name: Morlana
email: contact@morlana.net
- name: Florian Weber
email: f.weber@flweber.me
+1 -1
View File
@@ -15,7 +15,7 @@ aptly ({{ .Chart.AppVersion }}, chart {{ .Chart.Version }}) is deploying as {{ $
--- Check it's up ------------------------------------------------------- --- Check it's up -------------------------------------------------------
kubectl exec -n {{ .Release.Namespace }} {{ $fullname }}-0 -c aptly -- \ kubectl exec -n {{ .Release.Namespace }} {{ $fullname }}-0 -c aptly -- \
curl -fsS http://127.0.0.1:8080/api/ready curl -fsS http://127.0.0.1:{{ .Values.ports.aptly }}/api/ready
kubectl logs -n {{ .Release.Namespace }} job/{{ $fullname }}-reconcile kubectl logs -n {{ .Release.Namespace }} job/{{ $fullname }}-reconcile
# (only present right after install/upgrade in `hook` mode) # (only present right after install/upgrade in `hook` mode)
+20 -7
View File
@@ -73,6 +73,19 @@ confirms it's intended.
{{- end -}} {{- end -}}
{{- end -}} {{- end -}}
{{/*
Resolves to the ServiceAccount name the pod should bind to: a generated or
overridden name when serviceAccount.create is true, the explicit override
when false, or "" (falls back to the namespace's "default" SA) otherwise.
*/}}
{{- define "aptly.serviceAccountName" -}}
{{- if .Values.serviceAccount.create -}}
{{- default (include "aptly.fullname" .) .Values.serviceAccount.name -}}
{{- else -}}
{{- .Values.serviceAccount.name -}}
{{- end -}}
{{- end -}}
{{- define "aptly.imagePullSecrets" -}} {{- define "aptly.imagePullSecrets" -}}
{{- $secrets := concat (.Values.global.imagePullSecrets | default list) (.Values.image.pullSecrets | default list) -}} {{- $secrets := concat (.Values.global.imagePullSecrets | default list) (.Values.image.pullSecrets | default list) -}}
{{- if $secrets }} {{- if $secrets }}
@@ -94,7 +107,7 @@ templates/statefulset.yaml (the aptly config) and templates/ingress.yaml
{{- end -}} {{- end -}}
{{- define "aptly.apiListen" -}} {{- define "aptly.apiListen" -}}
{{- if .Values.proxy.enabled -}}127.0.0.1:8080{{- else -}}0.0.0.0:8080{{- end -}} {{- if .Values.proxy.enabled -}}127.0.0.1:{{ .Values.ports.aptly }}{{- else -}}0.0.0.0:{{ .Values.ports.aptly }}{{- end -}}
{{- end -}} {{- end -}}
{{/* {{/*
@@ -163,7 +176,7 @@ compose/config/nginx.*.conf for the same constraint hit empirically).
{{- $sec := include "aptly.security" . | fromJson -}} {{- $sec := include "aptly.security" . | fromJson -}}
{{- $p := .Values.proxy -}} {{- $p := .Values.proxy -}}
server { server {
listen 8080; listen {{ .Values.ports.nginx }};
server_name _; server_name _;
client_max_body_size {{ $p.maxUploadSize }}; client_max_body_size {{ $p.maxUploadSize }};
absolute_redirect off; absolute_redirect off;
@@ -176,8 +189,8 @@ server {
{{- end }} {{- end }}
location = /healthz { access_log off; return 200 "ok\n"; } location = /healthz { access_log off; return 200 "ok\n"; }
location = /api/ready { access_log off; proxy_pass http://127.0.0.1:8080; } location = /api/ready { access_log off; proxy_pass http://127.0.0.1:{{ .Values.ports.aptly }}; }
location = /api/healthy { access_log off; proxy_pass http://127.0.0.1:8080; } location = /api/healthy { access_log off; proxy_pass http://127.0.0.1:{{ .Values.ports.aptly }}; }
{{- if $sec.w }} {{- if $sec.w }}
location /api/ { location /api/ {
@@ -200,7 +213,7 @@ server {
{{- else }} {{- else }}
auth_basic off; auth_basic off;
{{- end }} {{- end }}
proxy_pass http://127.0.0.1:8080; proxy_pass http://127.0.0.1:{{ .Values.ports.aptly }};
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_request_buffering off; proxy_request_buffering off;
@@ -254,9 +267,9 @@ server {
# Separate, unauthenticated listener so scraping never needs the write-path # Separate, unauthenticated listener so scraping never needs the write-path
# credentials and a ServiceMonitor never needs a basicAuth secret. # credentials and a ServiceMonitor never needs a basicAuth secret.
server { server {
listen 9090; listen {{ .Values.ports.metrics }};
server_name _; server_name _;
location = /api/metrics { proxy_pass http://127.0.0.1:8080; } location = /api/metrics { proxy_pass http://127.0.0.1:{{ .Values.ports.aptly }}; }
location / { return 404; } location / { return 404; }
} }
{{- end }} {{- end }}
@@ -39,6 +39,10 @@ spec:
spec: spec:
restartPolicy: Never restartPolicy: Never
{{- include "aptly.imagePullSecrets" . | nindent 6 }} {{- include "aptly.imagePullSecrets" . | nindent 6 }}
{{- with include "aptly.serviceAccountName" . }}
serviceAccountName: {{ . }}
{{- end }}
automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }}
securityContext: securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }} {{- toYaml .Values.podSecurityContext | nindent 8 }}
containers: containers:
@@ -71,10 +75,14 @@ spec:
mountPath: /state.yaml mountPath: /state.yaml
subPath: state.yaml subPath: state.yaml
readOnly: true readOnly: true
- name: tmp
mountPath: /tmp
resources: resources:
{{- toYaml .Values.reconcile.resources | nindent 12 }} {{- toYaml .Values.reconcile.resources | nindent 12 }}
volumes: volumes:
- name: state - name: state
configMap: configMap:
name: {{ $fullname }}-state name: {{ $fullname }}-state
- name: tmp
emptyDir: {}
{{- end }} {{- end }}
+1 -1
View File
@@ -15,7 +15,7 @@ spec:
ingress: ingress:
{{- if .Values.networkPolicy.allowedNamespaces }} {{- if .Values.networkPolicy.allowedNamespaces }}
# Restricted to these namespaces (plus this one). NOTE: this applies to # Restricted to these namespaces (plus this one). NOTE: this applies to
# the whole nginx:8080 endpoint — read and write share one port, so this # the whole nginx endpoint — read and write share one port, so this
# cannot itself express "reads are public, writes are cluster-only" any # cannot itself express "reads are public, writes are cluster-only" any
# more precisely than security.write.allowCIDRs can (see the warning # more precisely than security.write.allowCIDRs can (see the warning
# rendered into nginx.conf for that). Use it to fence the Service off # rendered into nginx.conf for that). Use it to fence the Service off
@@ -0,0 +1,13 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "aptly.serviceAccountName" . }}
labels:
{{- include "aptly.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }}
{{- end -}}
+16 -6
View File
@@ -41,6 +41,10 @@ spec:
{{- end }} {{- end }}
spec: spec:
{{- include "aptly.imagePullSecrets" . | nindent 6 }} {{- include "aptly.imagePullSecrets" . | nindent 6 }}
{{- with include "aptly.serviceAccountName" . }}
serviceAccountName: {{ . }}
{{- end }}
automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }}
terminationGracePeriodSeconds: {{ .Values.workload.terminationGracePeriodSeconds }} terminationGracePeriodSeconds: {{ .Values.workload.terminationGracePeriodSeconds }}
securityContext: securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }} {{- toYaml .Values.podSecurityContext | nindent 8 }}
@@ -165,7 +169,7 @@ spec:
{{- end }} {{- end }}
ports: ports:
- name: aptly - name: aptly
containerPort: 8080 containerPort: {{ .Values.ports.aptly }}
volumeMounts: volumeMounts:
- name: data - name: data
mountPath: /var/lib/aptly mountPath: /var/lib/aptly
@@ -176,17 +180,23 @@ spec:
{{- with .Values.extraVolumeMounts }} {{- with .Values.extraVolumeMounts }}
{{- toYaml . | nindent 12 }} {{- toYaml . | nindent 12 }}
{{- end }} {{- end }}
# exec, not httpGet: aptly listens on 127.0.0.1 only (see
# aptly.apiListen) when proxy.enabled=true, but kubelet's httpGet
# probes connect to the POD IP, not localhost — that dials a
# loopback-only listener from outside and gets "connection
# refused". exec runs curl inside this container's own netns,
# where 127.0.0.1 is correct in either proxy mode.
startupProbe: startupProbe:
httpGet: { path: /api/ready, port: aptly } exec: { command: ["curl", "-fsS", "http://127.0.0.1:{{ .Values.ports.aptly }}/api/ready"] }
periodSeconds: {{ .Values.probes.startup.periodSeconds }} periodSeconds: {{ .Values.probes.startup.periodSeconds }}
failureThreshold: {{ .Values.probes.startup.failureThreshold }} failureThreshold: {{ .Values.probes.startup.failureThreshold }}
readinessProbe: readinessProbe:
httpGet: { path: /api/ready, port: aptly } exec: { command: ["curl", "-fsS", "http://127.0.0.1:{{ .Values.ports.aptly }}/api/ready"] }
periodSeconds: {{ .Values.probes.readiness.periodSeconds }} periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }} timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
failureThreshold: {{ .Values.probes.readiness.failureThreshold }} failureThreshold: {{ .Values.probes.readiness.failureThreshold }}
livenessProbe: livenessProbe:
httpGet: { path: /api/healthy, port: aptly } exec: { command: ["curl", "-fsS", "http://127.0.0.1:{{ .Values.ports.aptly }}/api/healthy"] }
periodSeconds: {{ .Values.probes.liveness.periodSeconds }} periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.liveness.timeoutSeconds }} timeoutSeconds: {{ .Values.probes.liveness.timeoutSeconds }}
failureThreshold: {{ .Values.probes.liveness.failureThreshold }} failureThreshold: {{ .Values.probes.liveness.failureThreshold }}
@@ -199,10 +209,10 @@ spec:
{{- toYaml .Values.nginx.securityContext | nindent 12 }} {{- toYaml .Values.nginx.securityContext | nindent 12 }}
ports: ports:
- name: http - name: http
containerPort: 8080 containerPort: {{ .Values.ports.nginx }}
{{- if .Values.metrics.service.enabled }} {{- if .Values.metrics.service.enabled }}
- name: metrics - name: metrics
containerPort: 9090 containerPort: {{ .Values.ports.metrics }}
{{- end }} {{- end }}
volumeMounts: volumeMounts:
- name: data - name: data
+19
View File
@@ -226,6 +226,15 @@
"publishEndpointName": { "type": "string" } "publishEndpointName": { "type": "string" }
} }
}, },
"ports": {
"type": "object",
"additionalProperties": false,
"properties": {
"aptly": { "type": "integer" },
"nginx": { "type": "integer" },
"metrics": { "type": "integer" }
}
},
"persistence": { "persistence": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
@@ -255,6 +264,16 @@
"podLabels": { "type": "object" } "podLabels": { "type": "object" }
} }
}, },
"serviceAccount": {
"type": "object",
"additionalProperties": false,
"properties": {
"create": { "type": "boolean" },
"name": { "type": "string" },
"annotations": { "type": "object" },
"automountServiceAccountToken": { "type": "boolean" }
}
},
"podSecurityContext": { "type": "object" }, "podSecurityContext": { "type": "object" },
"containerSecurityContext": { "type": "object" }, "containerSecurityContext": { "type": "object" },
"resources": { "type": "object" }, "resources": { "type": "object" },
+21
View File
@@ -176,6 +176,16 @@ proxy:
readTimeout: "3600s" readTimeout: "3600s"
publishEndpointName: public publishEndpointName: public
# Container ports inside the pod. aptly and nginx are two containers sharing
# one pod network namespace — they MUST NOT use the same port (nginx would
# fail to start with "address already in use"). These are internal-only;
# external access is still controlled by service.port / metrics.service.port,
# which stay decoupled from these via named ports (targetPort: http/metrics).
ports:
aptly: 8080 # aptly's own listener (loopback-only, or 0.0.0.0 when proxy.enabled=false)
nginx: 8081 # nginx's main (repo + API) listener
metrics: 9090 # nginx's /api/metrics passthrough listener, when metrics.service.enabled
persistence: persistence:
enabled: true enabled: true
existingClaim: "" # set this in production — see docs/operations.md existingClaim: "" # set this in production — see docs/operations.md
@@ -184,6 +194,17 @@ persistence:
size: 20Gi size: 20Gi
annotations: {} annotations: {}
serviceAccount:
# Own ServiceAccount per release, so RBAC (if you grant any) is scoped to
# this instance rather than the namespace's shared "default" identity.
create: true
# "" -> aptly.fullname (this release's name) when create=true. When
# create=false, set this to bind to a ServiceAccount you already manage;
# leave "" to fall back to the namespace's "default" ServiceAccount.
name: ""
annotations: {}
automountServiceAccountToken: true
workload: workload:
updateStrategy: updateStrategy:
type: RollingUpdate # safe here: a StatefulSet with replicas=1 always type: RollingUpdate # safe here: a StatefulSet with replicas=1 always
+1 -1
View File
@@ -64,7 +64,7 @@ LABEL org.opencontainers.image.title="aptly-server" \
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y --no-install-recommends \ apt-get update && apt-get install -y --no-install-recommends \
ca-certificates gnupg bzip2 xz-utils curl jq gettext-base openssl \ ca-certificates gnupg gpgv bzip2 xz-utils curl jq gettext-base openssl \
&& rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/apt/lists/* \
&& addgroup --system --gid 10001 aptly \ && addgroup --system --gid 10001 aptly \
&& adduser --system --uid 10001 --ingroup aptly --home /var/lib/aptly --disabled-password aptly \ && adduser --system --uid 10001 --ingroup aptly --home /var/lib/aptly --disabled-password aptly \