Skip to content

Commit 81c64c8

Browse files
committed
init helm chart for garage
1 parent fd86005 commit 81c64c8

16 files changed

Lines changed: 977 additions & 0 deletions

.github/workflows/release.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: Release Package
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
version:
7+
description: "Version (eg: x.x.x)"
8+
type: string
9+
required: true
10+
11+
env:
12+
CHART_NAME: garage
13+
OCI_IMAGE: oci://ghcr.io/${{ github.repository }}
14+
15+
jobs:
16+
build-and-publish:
17+
runs-on: ubuntu-24.04
18+
timeout-minutes: 15
19+
steps:
20+
- uses: actions/checkout@v5
21+
with:
22+
token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
23+
- uses: aboutbits/github-actions-base/git-setup@v2
24+
- name: Set up Helm
25+
uses: azure/setup-helm@v4
26+
with:
27+
version: 3.17.1
28+
- name: Set version of Chart.yaml file
29+
run: yq -i '.version = "${{ github.event.inputs.version }}"' ${{ env.CHART_NAME }}/Chart.yaml
30+
shell: bash
31+
- name: Package Helm chart
32+
run: helm package ${{ env.CHART_NAME }}
33+
shell: bash
34+
- name: Push to OCI registry
35+
run: |
36+
helm registry login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
37+
helm push ${{ env.CHART_NAME }}-${{ github.event.inputs.version }}.tgz ${{ env.OCI_IMAGE }}
38+
shell: bash
39+
- uses: aboutbits/github-actions-base/git-commit-and-push-all@v2
40+
with:
41+
message: '${{ github.event.inputs.version }}'
42+
- uses: aboutbits/github-actions-base/git-create-or-update-tag@v2
43+
with:
44+
tag-name: 'v${{ github.event.inputs.version }}'
45+
- uses: aboutbits/github-actions-base/github-create-release@v2
46+
with:
47+
tag-name: 'v${{ github.event.inputs.version }}'
48+
release-notes-generation: 'true'

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.idea
2+
*.iml
3+
*.tgz
4+
.DS_Store

garage/.helmignore

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Patterns to ignore when building packages.
2+
# This supports shell glob matching, relative path matching, and
3+
# negation (prefixed with !). Only one pattern per line.
4+
.DS_Store
5+
# Common VCS dirs
6+
.git/
7+
.gitignore
8+
.bzr/
9+
.bzrignore
10+
.hg/
11+
.hgignore
12+
.svn/
13+
# Common backup files
14+
*.swp
15+
*.bak
16+
*.tmp
17+
*.orig
18+
*~
19+
# Various IDEs
20+
.project
21+
.idea/
22+
*.tmproj
23+
.vscode/

garage/Chart.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
apiVersion: v2
2+
name: garage
3+
description: AboutBits single-node Garage (S3-compatible object storage) Helm chart
4+
type: application
5+
version: 0.1.0
6+
appVersion: "v2.3.0"
7+
keywords:
8+
- garage
9+
- s3
10+
- object-storage
11+
home: https://garagehq.deuxfleurs.fr
12+
sources:
13+
- https://git.deuxfleurs.fr/Deuxfleurs/garage

garage/templates/NOTES.txt

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
Garage single-node deployment "{{ include "garage.fullname" . }}" is starting up.
2+
3+
{{- if .Values.bootstrap.enabled }}
4+
5+
It bootstraps itself on first boot via `garage server --single-node --default-bucket`
6+
(no manual layout/bucket setup needed). This is a one-time bootstrap: adding a
7+
second bucket or rotating the default key later means calling the Admin API
8+
directly, not editing values.yaml.
9+
10+
Bucket: {{ .Values.bootstrap.defaultBucket }}
11+
Access key: (value of bootstrap.accessKey in your values)
12+
Secret key: (value of bootstrap.secretKey in your values)
13+
{{- else }}
14+
15+
No bucket/key is auto-created (bootstrap.enabled=false). Create buckets and
16+
keys yourself via the Admin API or the `garage` CLI, e.g.:
17+
kubectl -n {{ .Release.Namespace }} exec -it {{ include "garage.fullname" . }}-0 -- /garage layout assign -z dc1 -c 1G <node-id>
18+
kubectl -n {{ .Release.Namespace }} exec -it {{ include "garage.fullname" . }}-0 -- /garage layout apply --version 1
19+
kubectl -n {{ .Release.Namespace }} exec -it {{ include "garage.fullname" . }}-0 -- /garage bucket create <bucket-name>
20+
kubectl -n {{ .Release.Namespace }} exec -it {{ include "garage.fullname" . }}-0 -- /garage key create <key-name>
21+
kubectl -n {{ .Release.Namespace }} exec -it {{ include "garage.fullname" . }}-0 -- /garage bucket allow --read --write --owner <bucket-name> --key <key-name>
22+
{{- end }}
23+
24+
S3 endpoint (from inside the cluster):
25+
http://{{ include "garage.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.service.s3.port }}
26+
27+
Region: {{ .Values.s3.region }}
28+
29+
{{- if .Values.ingress.enabled }}
30+
31+
External S3 endpoint (via Ingress):
32+
{{- range .Values.ingress.hosts }}
33+
{{- $host := .host }}
34+
{{- range .paths }}
35+
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host }}{{ .path }}
36+
{{- end }}
37+
{{- end }}
38+
{{- end }}
39+
{{- if .Values.httpRoute.enabled }}
40+
41+
External S3 endpoint (via Gateway API HTTPRoute):
42+
{{- if .Values.httpRoute.hostnames }}
43+
{{- range .Values.httpRoute.hostnames }}
44+
https://{{ . }}/
45+
{{- end }}
46+
{{- else }}
47+
(matches all hostnames on the referenced Gateway)
48+
{{- end }}
49+
{{- end }}
50+
51+
Check status:
52+
kubectl -n {{ .Release.Namespace }} exec -it {{ include "garage.fullname" . }}-0 -- /garage status
53+
kubectl -n {{ .Release.Namespace }} exec -it {{ include "garage.fullname" . }}-0 -- /garage bucket list

garage/templates/_helpers.tpl

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{{/*
2+
Chart name and fullname
3+
*/}}
4+
{{- define "garage.name" -}}
5+
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
6+
{{- end }}
7+
8+
{{- define "garage.fullname" -}}
9+
{{- if .Values.fullnameOverride }}
10+
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
11+
{{- else }}
12+
{{- $name := default .Chart.Name .Values.nameOverride }}
13+
{{- if contains $name .Release.Name }}
14+
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
15+
{{- else }}
16+
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
17+
{{- end }}
18+
{{- end }}
19+
{{- end }}
20+
21+
{{- define "garage.chart" -}}
22+
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
23+
{{- end }}
24+
25+
{{- define "garage.labels" -}}
26+
helm.sh/chart: {{ include "garage.chart" . }}
27+
{{ include "garage.selectorLabels" . }}
28+
{{- if .Chart.AppVersion }}
29+
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
30+
{{- end }}
31+
app.kubernetes.io/managed-by: {{ .Release.Service }}
32+
{{- end }}
33+
34+
{{- define "garage.selectorLabels" -}}
35+
app.kubernetes.io/name: {{ include "garage.name" . }}
36+
app.kubernetes.io/instance: {{ .Release.Name }}
37+
{{- end }}
38+
39+
{{/*
40+
Name of the ServiceAccount to use.
41+
*/}}
42+
{{- define "garage.serviceAccountName" -}}
43+
{{- if .Values.serviceAccount.create }}
44+
{{- default (include "garage.fullname" .) .Values.serviceAccount.name }}
45+
{{- else }}
46+
{{- default "default" .Values.serviceAccount.name }}
47+
{{- end }}
48+
{{- end }}
49+
50+
{{/*
51+
Stable per-release RPC secret (inter-node clustering auth; loopback-only on a
52+
single node). `lookup` is used so that on `helm upgrade` we keep the previously
53+
generated value instead of rotating it (which would orphan the existing
54+
single-node cluster layout). The admin_token, by contrast, is a required,
55+
operator-provided value (see admin.token) so it is known up front.
56+
*/}}
57+
{{- define "garage.rpcSecret" -}}
58+
{{- $existing := (lookup "v1" "Secret" .Release.Namespace (include "garage.fullname" .)) -}}
59+
{{- if $existing -}}
60+
{{- index $existing.data "rpc_secret" | b64dec -}}
61+
{{- else -}}
62+
{{- sha256sum (printf "%s-rpc-%s" .Release.Name (randAlphaNum 32)) -}}
63+
{{- end -}}
64+
{{- end }}

garage/templates/httproute.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{{- if and .Values.ingress.enabled .Values.httpRoute.enabled -}}
2+
{{- fail "Enable only one of ingress.enabled or httpRoute.enabled, not both" -}}
3+
{{- end -}}
4+
{{- if .Values.httpRoute.enabled -}}
5+
{{- if not .Values.httpRoute.parentRefs -}}
6+
{{- fail "httpRoute.parentRefs is required when httpRoute.enabled=true" -}}
7+
{{- end -}}
8+
{{- $fullName := include "garage.fullname" . -}}
9+
apiVersion: gateway.networking.k8s.io/v1
10+
kind: HTTPRoute
11+
metadata:
12+
name: {{ $fullName }}
13+
labels:
14+
{{- include "garage.labels" . | nindent 4 }}
15+
{{- with .Values.httpRoute.labels }}
16+
{{- toYaml . | nindent 4 }}
17+
{{- end }}
18+
{{- with .Values.httpRoute.annotations }}
19+
annotations:
20+
{{- toYaml . | nindent 4 }}
21+
{{- end }}
22+
spec:
23+
parentRefs:
24+
{{- toYaml .Values.httpRoute.parentRefs | nindent 4 }}
25+
{{- with .Values.httpRoute.hostnames }}
26+
hostnames:
27+
{{- toYaml . | nindent 4 }}
28+
{{- end }}
29+
rules:
30+
- matches:
31+
{{- toYaml .Values.httpRoute.matches | nindent 8 }}
32+
backendRefs:
33+
- name: {{ $fullName }}
34+
port: {{ .Values.service.s3.port }}
35+
{{- end }}

garage/templates/ingress.yaml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{{- if .Values.ingress.enabled -}}
2+
{{- $fullName := include "garage.fullname" . -}}
3+
{{- $svcPort := .Values.service.s3.port -}}
4+
apiVersion: networking.k8s.io/v1
5+
kind: Ingress
6+
metadata:
7+
name: {{ $fullName }}
8+
labels:
9+
{{- include "garage.labels" . | nindent 4 }}
10+
{{- with .Values.ingress.labels }}
11+
{{- toYaml . | nindent 4 }}
12+
{{- end }}
13+
{{- with .Values.ingress.annotations }}
14+
annotations:
15+
{{- toYaml . | nindent 4 }}
16+
{{- end }}
17+
spec:
18+
{{- with .Values.ingress.className }}
19+
ingressClassName: {{ . }}
20+
{{- end }}
21+
{{- if .Values.ingress.tls }}
22+
tls:
23+
{{- range .Values.ingress.tls }}
24+
- hosts:
25+
{{- range .hosts }}
26+
- {{ . | quote }}
27+
{{- end }}
28+
secretName: {{ .secretName }}
29+
{{- end }}
30+
{{- end }}
31+
rules:
32+
{{- range .Values.ingress.hosts }}
33+
- host: {{ .host | quote }}
34+
http:
35+
paths:
36+
{{- range .paths }}
37+
- path: {{ .path }}
38+
pathType: {{ .pathType }}
39+
backend:
40+
service:
41+
name: {{ $fullName }}
42+
port:
43+
number: {{ $svcPort }}
44+
{{- end }}
45+
{{- end }}
46+
{{- end }}

garage/templates/secret.yaml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{{- $rpcSecret := include "garage.rpcSecret" . -}}
2+
{{- $adminToken := "" -}}
3+
{{- if .Values.admin.enabled -}}
4+
{{- $adminToken = required "admin.token is required when admin.enabled=true" .Values.admin.token -}}
5+
{{- end -}}
6+
apiVersion: v1
7+
kind: Secret
8+
metadata:
9+
name: {{ include "garage.fullname" . }}
10+
labels:
11+
{{- include "garage.labels" . | nindent 4 }}
12+
type: Opaque
13+
stringData:
14+
rpc_secret: {{ $rpcSecret | quote }}
15+
{{- if .Values.admin.enabled }}
16+
admin_token: {{ $adminToken | quote }}
17+
{{- end }}
18+
{{- if .Values.bootstrap.enabled }}
19+
default_access_key: {{ required "bootstrap.accessKey is required when bootstrap.enabled=true (e.g. GK + 16 hex chars)" .Values.bootstrap.accessKey | quote }}
20+
default_secret_key: {{ required "bootstrap.secretKey is required when bootstrap.enabled=true" .Values.bootstrap.secretKey | quote }}
21+
{{- end }}
22+
garage.toml: |
23+
metadata_dir = "/var/lib/garage/meta"
24+
data_dir = "/var/lib/garage/data"
25+
db_engine = {{ .Values.garage.dbEngine | quote }}
26+
block_size = {{ .Values.garage.blockSize | quote }}
27+
{{- if eq (toString .Values.garage.compressionLevel) "none" }}
28+
compression_level = "none"
29+
{{- else }}
30+
compression_level = {{ .Values.garage.compressionLevel | int }}
31+
{{- end }}
32+
33+
replication_factor = 1
34+
35+
{{- if .Values.garage.metadataAutoSnapshotInterval }}
36+
metadata_auto_snapshot_interval = {{ .Values.garage.metadataAutoSnapshotInterval | quote }}
37+
{{- end }}
38+
39+
rpc_bind_addr = "[::]:3901"
40+
rpc_public_addr = "127.0.0.1:3901"
41+
rpc_secret = {{ $rpcSecret | quote }}
42+
43+
{{- if .Values.garage.additionalTopLevelConfig }}
44+
{{ .Values.garage.additionalTopLevelConfig | nindent 4 }}
45+
{{- end }}
46+
47+
[s3_api]
48+
s3_region = {{ .Values.s3.region | quote }}
49+
api_bind_addr = "[::]:{{ .Values.service.s3.port }}"
50+
root_domain = {{ .Values.s3.rootDomain | quote }}
51+
52+
{{- if .Values.admin.enabled }}
53+
54+
[admin]
55+
api_bind_addr = "[::]:{{ .Values.admin.port }}"
56+
admin_token = {{ $adminToken | quote }}
57+
{{- end }}

garage/templates/service.yaml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
name: {{ include "garage.fullname" . }}
5+
labels:
6+
{{- include "garage.labels" . | nindent 4 }}
7+
{{- with .Values.service.annotations }}
8+
annotations:
9+
{{- toYaml . | nindent 4 }}
10+
{{- end }}
11+
spec:
12+
# Not headless: single-node Garage has no peers to discover via pod DNS, so
13+
# this plain ClusterIP Service is used both as the StatefulSet's
14+
# `serviceName` and as the address clients connect to for the S3 API.
15+
type: {{ .Values.service.type }}
16+
selector:
17+
{{- include "garage.selectorLabels" . | nindent 4 }}
18+
ports:
19+
- name: s3
20+
port: {{ .Values.service.s3.port }}
21+
targetPort: s3
22+
{{- if .Values.admin.enabled }}
23+
- name: admin
24+
port: {{ .Values.admin.port }}
25+
targetPort: admin
26+
{{- end }}
27+
{{- if and .Values.admin.enabled .Values.monitoring.metrics.enabled }}
28+
---
29+
apiVersion: v1
30+
kind: Service
31+
metadata:
32+
name: {{ include "garage.fullname" . }}-metrics
33+
labels:
34+
{{- include "garage.labels" . | nindent 4 }}
35+
annotations:
36+
prometheus.io/scrape: "true"
37+
spec:
38+
type: ClusterIP
39+
clusterIP: None
40+
ports:
41+
- name: metrics
42+
port: {{ .Values.admin.port }}
43+
targetPort: admin
44+
selector:
45+
{{- include "garage.selectorLabels" . | nindent 4 }}
46+
{{- end }}

0 commit comments

Comments
 (0)