Public Access
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1675ea9a4e
|
||
|
|
5af33e9128
|
@@ -82,10 +82,10 @@ jobs:
|
|||||||
- name: Package and push a throwaway chart version
|
- name: Package and push a throwaway chart version
|
||||||
run: |
|
run: |
|
||||||
helm package charts/aptly --version 0.0.0-preflight --app-version preflight
|
helm package charts/aptly --version 0.0.0-preflight --app-version preflight
|
||||||
helm push aptly-0.0.0-preflight.tgz oci://git.morlana.online/f.weber
|
helm push aptly-0.0.0-preflight.tgz oci://git.morlana.online/f.weber/charts
|
||||||
- name: Verify it is pullable
|
- name: Verify it is pullable
|
||||||
run: |
|
run: |
|
||||||
helm show chart oci://git.morlana.online/f.weber/aptly --version 0.0.0-preflight
|
helm show chart oci://git.morlana.online/f.weber/charts/aptly --version 0.0.0-preflight
|
||||||
|
|
||||||
issues-api:
|
issues-api:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
|
|||||||
@@ -1,17 +1,33 @@
|
|||||||
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
|
||||||
|
# package registry stores both container images and Helm OCI charts as
|
||||||
|
# generic OCI artifacts, and a chart sharing the exact repository path with
|
||||||
|
# the container image of the same name makes the package listing/type
|
||||||
|
# ambiguous. A distinct `charts/` path keeps the two package kinds apart.
|
||||||
on:
|
on:
|
||||||
push:
|
release:
|
||||||
tags:
|
types: [published]
|
||||||
- 'chart/v*'
|
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: release-chart
|
group: release-chart
|
||||||
@@ -19,15 +35,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:
|
||||||
@@ -43,11 +60,16 @@ jobs:
|
|||||||
|
|
||||||
- name: Idempotency check — refuse to overwrite an existing chart version
|
- name: Idempotency check — refuse to overwrite an existing chart version
|
||||||
run: |
|
run: |
|
||||||
if helm show chart "oci://git.morlana.online/f.weber/aptly" --version "${{ steps.version.outputs.version }}" >/dev/null 2>&1; then
|
if helm show chart "oci://git.morlana.online/f.weber/charts/aptly" --version "${{ steps.version.outputs.version }}" >/dev/null 2>&1; then
|
||||||
echo "::error::chart version ${{ steps.version.outputs.version }} already exists in the registry. Bump the version and re-tag — this workflow never overwrites a published chart."
|
echo "::error::chart version ${{ steps.version.outputs.version }} already exists in the registry. Bump the version and re-tag — this workflow never overwrites a published chart."
|
||||||
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:
|
||||||
@@ -80,15 +102,16 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "${{ secrets.REGISTRY_TOKEN }}" | helm registry login git.morlana.online \
|
echo "${{ secrets.REGISTRY_TOKEN }}" | helm registry login git.morlana.online \
|
||||||
--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
|
helm push "aptly-${{ steps.version.outputs.version }}.tgz" oci://git.morlana.online/f.weber/charts
|
||||||
|
|
||||||
- name: Create Gitea release with chart artifacts
|
- 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/aptly --version ${{ steps.version.outputs.version }}`
|
`helm pull oci://git.morlana.online/f.weber/charts/aptly --version ${{ steps.version.outputs.version }}`
|
||||||
files: |
|
files: |
|
||||||
aptly-${{ steps.version.outputs.version }}.tgz
|
aptly-${{ steps.version.outputs.version }}.tgz
|
||||||
aptly-${{ steps.version.outputs.version }}.tgz.prov
|
aptly-${{ steps.version.outputs.version }}.tgz.prov
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ curl http://localhost:8080/api/ready
|
|||||||
Details and the production path: [docs/quickstart-compose.md](docs/quickstart-compose.md).
|
Details and the production path: [docs/quickstart-compose.md](docs/quickstart-compose.md).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
helm install aptly oci://git.morlana.online/f.weber/aptly --version <version>
|
helm install aptly oci://git.morlana.online/f.weber/charts/aptly --version <version>
|
||||||
```
|
```
|
||||||
|
|
||||||
Details: [docs/quickstart-helm.md](docs/quickstart-helm.md).
|
Details: [docs/quickstart-helm.md](docs/quickstart-helm.md).
|
||||||
|
|||||||
+11
-2
@@ -5,7 +5,7 @@ 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.0
|
||||||
appVersion: "1.6.3-1"
|
appVersion: "1.6.3-1"
|
||||||
home: https://git.morlana.online/f.weber/aptly-containerized
|
home: https://git.morlana.online/f.weber/aptly-containerized
|
||||||
sources:
|
sources:
|
||||||
@@ -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,12 @@ 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: Configurable ServiceAccount (serviceAccount.create/name/annotations/automountServiceAccountToken), defaulting to a dedicated ServiceAccount per release.
|
||||||
|
- kind: added
|
||||||
|
description: Configurable internal container ports (ports.aptly/nginx/metrics).
|
||||||
|
- kind: fixed
|
||||||
|
description: nginx and aptly no longer both listen on port 8080 inside the same pod, which made nginx fail to start with "address already in use". nginx now defaults to 8081 internally; external service.port is unchanged.
|
||||||
|
- kind: changed
|
||||||
|
description: Chart releases are now triggered by publishing a Gitea Release (instead of a bare tag push), so pre-releases can be flagged for ArtifactHub.
|
||||||
|
|||||||
+15
-3
@@ -8,7 +8,7 @@ no library-chart dependency, no concepts to learn beyond aptly's and Kubernetes'
|
|||||||
## TL;DR
|
## TL;DR
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
helm install my-aptly oci://git.morlana.online/f.weber/aptly --version <version>
|
helm install my-aptly oci://git.morlana.online/f.weber/charts/aptly --version <version>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
@@ -45,7 +45,7 @@ Three things this chart is built around:
|
|||||||
## Installing the chart
|
## Installing the chart
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
helm install my-aptly oci://git.morlana.online/f.weber/aptly --version <version> \
|
helm install my-aptly oci://git.morlana.online/f.weber/charts/aptly --version <version> \
|
||||||
--set ingress.enabled=true \
|
--set ingress.enabled=true \
|
||||||
--set ingress.repo.host=apt.example.com
|
--set ingress.repo.host=apt.example.com
|
||||||
```
|
```
|
||||||
@@ -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` | `""` | |
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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 -}}
|
||||||
@@ -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
|
||||||
@@ -199,10 +203,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
|
||||||
|
|||||||
@@ -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" },
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
+11
-10
@@ -1,11 +1,13 @@
|
|||||||
# Public keys
|
# Public keys
|
||||||
|
|
||||||
## Chart signing key (needed, not yet set up)
|
## Chart signing key
|
||||||
|
|
||||||
`.gitea/workflows/release-chart.yaml` runs `helm package --sign` to produce a
|
`.gitea/workflows/release-chart.yaml` runs `helm package --sign` to produce a
|
||||||
`.tgz.prov` file for every chart release, and `charts/aptly/Chart.yaml` is meant to
|
`.tgz.prov` file for every chart release, and `charts/aptly/Chart.yaml` carries an
|
||||||
carry an `artifacthub.io/signKey` annotation pointing at the public half of that
|
`artifacthub.io/signKey` annotation pointing at `chart-signing.asc` in this
|
||||||
key (both are currently commented out / referencing a placeholder — see below).
|
directory (fingerprint `FC35C0FAA26605C4C21C7BBFBF43884145E5AA94`). The matching
|
||||||
|
private key is stored as the `GPG_PRIVATE_KEY`/`GPG_PASSPHRASE`/`GPG_KEY_ID`
|
||||||
|
secrets in this repo's Gitea settings.
|
||||||
|
|
||||||
**This key must NOT be Ed25519/EdDSA.** Helm's chart signing is built on the
|
**This key must NOT be Ed25519/EdDSA.** Helm's chart signing is built on the
|
||||||
deprecated `golang.org/x/crypto/openpgp` library, which cannot read Ed25519 keys at
|
deprecated `golang.org/x/crypto/openpgp` library, which cannot read Ed25519 keys at
|
||||||
@@ -25,7 +27,7 @@ Ed25519 — see [docs/packaging.md](../docs/packaging.md#gpg) — just not for
|
|||||||
`helm package --sign`. This repo therefore needs its own, separate, RSA key
|
`helm package --sign`. This repo therefore needs its own, separate, RSA key
|
||||||
dedicated to chart-package signing.
|
dedicated to chart-package signing.
|
||||||
|
|
||||||
### Generating it
|
### Generating a replacement (e.g. on rotation)
|
||||||
|
|
||||||
Run this yourself (locally, not in CI) so the private key material never has to
|
Run this yourself (locally, not in CI) so the private key material never has to
|
||||||
pass through anything but your own machine and the Gitea secrets store:
|
pass through anything but your own machine and the Gitea secrets store:
|
||||||
@@ -44,16 +46,15 @@ gpg --armor --export <key-id> > pubkeys/chart-signing.asc
|
|||||||
gpg --armor --export-secret-keys <key-id> # -> paste as GPG_PRIVATE_KEY
|
gpg --armor --export-secret-keys <key-id> # -> paste as GPG_PRIVATE_KEY
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, in the repo's Gitea settings:
|
Then, in the repo's Gitea settings, update:
|
||||||
- Secret **`GPG_PRIVATE_KEY`** — the armored output of the last command above
|
- Secret **`GPG_PRIVATE_KEY`** — the armored output of the last command above
|
||||||
- Secret **`GPG_PASSPHRASE`** — whatever passphrase you set (empty string if none)
|
- Secret **`GPG_PASSPHRASE`** — whatever passphrase you set (empty string if none)
|
||||||
- Secret **`GPG_KEY_ID`** — the key ID or fingerprint from `gpg --list-secret-keys`
|
- Secret **`GPG_KEY_ID`** — the key ID or fingerprint from `gpg --list-secret-keys`
|
||||||
|
|
||||||
Commit `pubkeys/chart-signing.asc`, then uncomment the `artifacthub.io/signKey`
|
Commit the new `pubkeys/chart-signing.asc` over the old one, and update the
|
||||||
block in `charts/aptly/Chart.yaml` with the real fingerprint, and uncomment
|
fingerprint in the `artifacthub.io/signKey` annotation in `charts/aptly/Chart.yaml`.
|
||||||
`pubkeys/chart-signing.asc` in `release-chart.yaml`'s release-assets step.
|
|
||||||
|
|
||||||
### Verifying a downloaded chart (once the key exists)
|
### Verifying a downloaded chart
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
gpg --import pubkeys/chart-signing.asc
|
gpg --import pubkeys/chart-signing.asc
|
||||||
|
|||||||
Reference in New Issue
Block a user