-
Notifications
You must be signed in to change notification settings - Fork 0
Setup helm chart for garage #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
81c64c8
3ff2e06
9f31d5a
11a4597
e2fccab
6bed931
2a1d059
35fa35f
ab7ae91
8aa055c
87ab5f8
ac3bd6c
255b546
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| name: Release Package | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| version: | ||
| description: "Version (eg: x.x.x)" | ||
| type: string | ||
| required: true | ||
|
|
||
| env: | ||
| CHART_NAME: garage | ||
| OCI_IMAGE: oci://ghcr.io/${{ github.repository }} | ||
|
|
||
| jobs: | ||
| build-and-publish: | ||
| runs-on: ubuntu-24.04 | ||
| timeout-minutes: 15 | ||
| steps: | ||
| - uses: actions/checkout@v5 | ||
|
stplasim marked this conversation as resolved.
Outdated
|
||
| with: | ||
| token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }} | ||
| - uses: aboutbits/github-actions-base/git-setup@v2 | ||
| - name: Set up Helm | ||
| uses: azure/setup-helm@v4 | ||
|
stplasim marked this conversation as resolved.
Outdated
|
||
| with: | ||
| version: 3.21.3 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As Helm v4 is out since November 2025, we should think of upgrading to v4 in the future. v3 is fine for now.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can go with helm 4. There are no breaking changes that we care about |
||
| - name: Set version of Chart.yaml file | ||
| run: yq -i '.version = "${{ github.event.inputs.version }}"' ${{ env.CHART_NAME }}/Chart.yaml | ||
| shell: bash | ||
| - name: Package Helm chart | ||
| run: helm package ${{ env.CHART_NAME }} | ||
| shell: bash | ||
| - name: Push to OCI registry | ||
| run: | | ||
| helm registry login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }} | ||
| helm push ${{ env.CHART_NAME }}-${{ github.event.inputs.version }}.tgz ${{ env.OCI_IMAGE }} | ||
| shell: bash | ||
| - uses: aboutbits/github-actions-base/git-commit-and-push-all@v2 | ||
| with: | ||
| message: '${{ github.event.inputs.version }}' | ||
| - uses: aboutbits/github-actions-base/git-create-or-update-tag@v2 | ||
| with: | ||
| tag-name: 'v${{ github.event.inputs.version }}' | ||
| - uses: aboutbits/github-actions-base/github-create-release@v2 | ||
| with: | ||
| tag-name: 'v${{ github.event.inputs.version }}' | ||
| release-notes-generation: 'true' | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| .idea | ||
| *.iml | ||
| *.tgz | ||
| .DS_Store |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| # Patterns to ignore when building packages. | ||
| # This supports shell glob matching, relative path matching, and | ||
| # negation (prefixed with !). Only one pattern per line. | ||
| .DS_Store | ||
| # Common VCS dirs | ||
| .git/ | ||
| .gitignore | ||
| .bzr/ | ||
| .bzrignore | ||
| .hg/ | ||
| .hgignore | ||
| .svn/ | ||
| # Common backup files | ||
| *.swp | ||
| *.bak | ||
| *.tmp | ||
| *.orig | ||
| *~ | ||
| # Various IDEs | ||
| .project | ||
| .idea/ | ||
| *.tmproj | ||
| .vscode/ |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,13 @@ | ||||||||||||
| apiVersion: v2 | ||||||||||||
| name: garage | ||||||||||||
| description: AboutBits single-node Garage (S3-compatible object storage) Helm chart | ||||||||||||
| type: application | ||||||||||||
| version: 0.1.0 | ||||||||||||
| appVersion: "v2.3.0" | ||||||||||||
|
stplasim marked this conversation as resolved.
Outdated
|
||||||||||||
| keywords: | ||||||||||||
| - garage | ||||||||||||
| - s3 | ||||||||||||
| - object-storage | ||||||||||||
| home: https://garagehq.deuxfleurs.fr | ||||||||||||
| sources: | ||||||||||||
| - https://git.deuxfleurs.fr/Deuxfleurs/garage | ||||||||||||
|
Comment on lines
+12
to
+13
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's also add the GitHub URL.
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, even though datahub-local is the wrong repo, we can put their GitHub mirror here. |
||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| Garage single-node deployment "{{ include "garage.fullname" . }}" is starting up. | ||
|
|
||
| {{- if .Values.bootstrap.enabled }} | ||
|
|
||
| It bootstraps itself on first boot via `garage server --single-node --default-bucket` | ||
| (no manual layout/bucket setup needed). This is a one-time bootstrap: adding a | ||
| second bucket or rotating the default key later means calling the Admin API | ||
| directly, not editing values.yaml. | ||
|
|
||
| Bucket: {{ .Values.bootstrap.defaultBucket }} | ||
| Access key: (value of bootstrap.accessKey in your values) | ||
| Secret key: (value of bootstrap.secretKey in your values) | ||
| {{- else }} | ||
|
|
||
| No bucket/key is auto-created (bootstrap.enabled=false). Create buckets and | ||
| keys yourself via the Admin API or the `garage` CLI, e.g.: | ||
| kubectl -n {{ .Release.Namespace }} exec -it {{ include "garage.fullname" . }}-0 -- /garage layout assign -z dc1 -c 1G <node-id> | ||
| kubectl -n {{ .Release.Namespace }} exec -it {{ include "garage.fullname" . }}-0 -- /garage layout apply --version 1 | ||
| kubectl -n {{ .Release.Namespace }} exec -it {{ include "garage.fullname" . }}-0 -- /garage bucket create <bucket-name> | ||
| kubectl -n {{ .Release.Namespace }} exec -it {{ include "garage.fullname" . }}-0 -- /garage key create <key-name> | ||
| kubectl -n {{ .Release.Namespace }} exec -it {{ include "garage.fullname" . }}-0 -- /garage bucket allow --read --write --owner <bucket-name> --key <key-name> | ||
| {{- end }} | ||
|
|
||
| S3 endpoint (from inside the cluster): | ||
| http://{{ include "garage.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.service.s3.port }} | ||
|
|
||
| Region: {{ .Values.s3.region }} | ||
|
|
||
| {{- if .Values.ingress.enabled }} | ||
|
|
||
| External S3 endpoint (via Ingress): | ||
| {{- range .Values.ingress.hosts }} | ||
| {{- $host := .host }} | ||
| {{- range .paths }} | ||
| http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host }}{{ .path }} | ||
| {{- end }} | ||
| {{- end }} | ||
| {{- end }} | ||
| {{- if .Values.httpRoute.enabled }} | ||
|
|
||
| External S3 endpoint (via Gateway API HTTPRoute): | ||
| {{- if .Values.httpRoute.hostnames }} | ||
| {{- range .Values.httpRoute.hostnames }} | ||
| https://{{ . }}/ | ||
| {{- end }} | ||
| {{- else }} | ||
| (matches all hostnames on the referenced Gateway) | ||
| {{- end }} | ||
| {{- end }} | ||
|
|
||
| Check status: | ||
| kubectl -n {{ .Release.Namespace }} exec -it {{ include "garage.fullname" . }}-0 -- /garage status | ||
| kubectl -n {{ .Release.Namespace }} exec -it {{ include "garage.fullname" . }}-0 -- /garage bucket list |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| {{/* | ||
| Chart name and fullname | ||
| */}} | ||
| {{- define "garage.name" -}} | ||
| {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} | ||
| {{- end }} | ||
|
|
||
| {{- define "garage.fullname" -}} | ||
| {{- if .Values.fullnameOverride }} | ||
| {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} | ||
| {{- else }} | ||
| {{- $name := default .Chart.Name .Values.nameOverride }} | ||
| {{- if contains $name .Release.Name }} | ||
| {{- .Release.Name | trunc 63 | trimSuffix "-" }} | ||
| {{- else }} | ||
| {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} | ||
| {{- end }} | ||
| {{- end }} | ||
| {{- end }} | ||
|
|
||
| {{- define "garage.chart" -}} | ||
| {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} | ||
| {{- end }} | ||
|
|
||
| {{- define "garage.labels" -}} | ||
| helm.sh/chart: {{ include "garage.chart" . }} | ||
| {{ include "garage.selectorLabels" . }} | ||
| {{- if .Chart.AppVersion }} | ||
| app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} | ||
| {{- end }} | ||
| app.kubernetes.io/managed-by: {{ .Release.Service }} | ||
| {{- end }} | ||
|
|
||
| {{- define "garage.selectorLabels" -}} | ||
| app.kubernetes.io/name: {{ include "garage.name" . }} | ||
| app.kubernetes.io/instance: {{ .Release.Name }} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| Name of the ServiceAccount to use. | ||
| */}} | ||
| {{- define "garage.serviceAccountName" -}} | ||
| {{- if .Values.serviceAccount.create }} | ||
| {{- $baseName := .Values.serviceAccount.name | default (printf "sa-%s" (include "garage.fullname" .)) -}} | ||
| {{- $baseName | trunc 63 | trimSuffix "-" }} | ||
| {{- else }} | ||
| {{- default "default" .Values.serviceAccount.name }} | ||
| {{- end }} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| Names for the ingress / HTTPRoute objects (prefixed for readability, overridable | ||
| via .Values.<kind>.name). These name the routing object only — the backend still | ||
| targets the Service (garage.fullname). | ||
| */}} | ||
| {{- define "garage.ingressName" -}} | ||
| {{- $baseName := .Values.ingress.name | default (printf "ingress-%s" (include "garage.fullname" .)) -}} | ||
| {{- $baseName | trunc 63 | trimSuffix "-" }} | ||
| {{- end }} | ||
|
|
||
| {{- define "garage.httpRouteName" -}} | ||
| {{- $baseName := .Values.httpRoute.name | default (printf "httproute-%s" (include "garage.fullname" .)) -}} | ||
| {{- $baseName | trunc 63 | trimSuffix "-" }} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| Auto-generated RPC secret (inter-node clustering auth; loopback-only on a single | ||
| node, so it is never consumed externally). `lookup` reuses the previously stored | ||
| value on `helm upgrade` to avoid a needless Secret diff — this is churn | ||
| avoidance, not a correctness requirement: the node's identity and cluster layout | ||
| live in the metadata volume, independent of this secret, so regenerating it does | ||
| not lose data or orphan the layout. The admin_token, by contrast, is a required, | ||
| operator-provided value (see admin.token) so it is known up front. | ||
| */}} | ||
| {{- define "garage.rpcSecret" -}} | ||
| {{- $existing := (lookup "v1" "Secret" .Release.Namespace (include "garage.fullname" .)) -}} | ||
| {{- if $existing -}} | ||
| {{- index $existing.data "rpc_secret" | b64dec -}} | ||
| {{- else -}} | ||
| {{- sha256sum (printf "%s-rpc-%s" .Release.Name (randAlphaNum 32)) -}} | ||
| {{- end -}} | ||
| {{- end }} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| {{- if and .Values.ingress.enabled .Values.httpRoute.enabled -}} | ||
| {{- fail "Enable only one of ingress.enabled or httpRoute.enabled, not both" -}} | ||
| {{- end -}} | ||
| {{- if .Values.httpRoute.enabled -}} | ||
| {{- if not .Values.httpRoute.parentRefs -}} | ||
| {{- fail "httpRoute.parentRefs is required when httpRoute.enabled=true" -}} | ||
| {{- end -}} | ||
| {{- $fullName := include "garage.fullname" . -}} | ||
| apiVersion: gateway.networking.k8s.io/v1 | ||
| kind: HTTPRoute | ||
| metadata: | ||
| name: {{ include "garage.httpRouteName" . }} | ||
| labels: | ||
| {{- include "garage.labels" . | nindent 4 }} | ||
| {{- with .Values.httpRoute.labels }} | ||
| {{- toYaml . | nindent 4 }} | ||
| {{- end }} | ||
| {{- with .Values.httpRoute.annotations }} | ||
| annotations: | ||
| {{- toYaml . | nindent 4 }} | ||
| {{- end }} | ||
| spec: | ||
| parentRefs: | ||
| {{- toYaml .Values.httpRoute.parentRefs | nindent 4 }} | ||
| {{- with .Values.httpRoute.hostnames }} | ||
| hostnames: | ||
| {{- toYaml . | nindent 4 }} | ||
| {{- end }} | ||
| rules: | ||
| - matches: | ||
| {{- toYaml .Values.httpRoute.matches | nindent 8 }} | ||
| backendRefs: | ||
| - name: {{ $fullName }} | ||
| port: {{ .Values.service.s3.port }} | ||
| {{- end }} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| {{- if .Values.ingress.enabled -}} | ||
| {{- $fullName := include "garage.fullname" . -}} | ||
| {{- $svcPort := .Values.service.s3.port -}} | ||
| apiVersion: networking.k8s.io/v1 | ||
| kind: Ingress | ||
| metadata: | ||
| name: {{ include "garage.ingressName" . }} | ||
| labels: | ||
| {{- include "garage.labels" . | nindent 4 }} | ||
| {{- with .Values.ingress.labels }} | ||
| {{- toYaml . | nindent 4 }} | ||
| {{- end }} | ||
| {{- with .Values.ingress.annotations }} | ||
| annotations: | ||
| {{- toYaml . | nindent 4 }} | ||
| {{- end }} | ||
| spec: | ||
| {{- with .Values.ingress.className }} | ||
| ingressClassName: {{ . }} | ||
| {{- end }} | ||
| {{- if .Values.ingress.tls }} | ||
| tls: | ||
| {{- range .Values.ingress.tls }} | ||
| - hosts: | ||
| {{- range .hosts }} | ||
| - {{ . | quote }} | ||
| {{- end }} | ||
| secretName: {{ .secretName }} | ||
| {{- end }} | ||
| {{- end }} | ||
| rules: | ||
| {{- range .Values.ingress.hosts }} | ||
| - host: {{ .host | quote }} | ||
| http: | ||
| paths: | ||
| {{- range .paths }} | ||
| - path: {{ .path }} | ||
| pathType: {{ .pathType }} | ||
| backend: | ||
| service: | ||
| name: {{ $fullName }} | ||
| port: | ||
| number: {{ $svcPort }} | ||
| {{- end }} | ||
| {{- end }} | ||
| {{- end }} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| {{- $rpcSecret := include "garage.rpcSecret" . -}} | ||
| {{- $adminToken := "" -}} | ||
| {{- if .Values.admin.enabled -}} | ||
| {{- $adminToken = required "admin.token is required when admin.enabled=true" .Values.admin.token -}} | ||
| {{- end -}} | ||
| apiVersion: v1 | ||
| kind: Secret | ||
| metadata: | ||
| name: {{ include "garage.fullname" . }} | ||
| labels: | ||
| {{- include "garage.labels" . | nindent 4 }} | ||
| type: Opaque | ||
| stringData: | ||
| rpc_secret: {{ $rpcSecret | quote }} | ||
| {{- if .Values.admin.enabled }} | ||
| admin_token: {{ $adminToken | quote }} | ||
| {{- end }} | ||
| {{- if .Values.bootstrap.enabled }} | ||
| default_access_key: {{ required "bootstrap.accessKey is required when bootstrap.enabled=true (e.g. GK + 16 hex chars)" .Values.bootstrap.accessKey | quote }} | ||
| default_secret_key: {{ required "bootstrap.secretKey is required when bootstrap.enabled=true" .Values.bootstrap.secretKey | quote }} | ||
| {{- end }} | ||
| garage.toml: | | ||
| metadata_dir = "/var/lib/garage/meta" | ||
| data_dir = "/var/lib/garage/data" | ||
| db_engine = {{ .Values.garage.dbEngine | quote }} | ||
| block_size = {{ .Values.garage.blockSize | quote }} | ||
| {{- if eq (toString .Values.garage.compressionLevel) "none" }} | ||
| compression_level = "none" | ||
| {{- else }} | ||
| compression_level = {{ .Values.garage.compressionLevel | int }} | ||
| {{- end }} | ||
|
|
||
| replication_factor = 1 | ||
|
|
||
| {{- if .Values.garage.metadataAutoSnapshotInterval }} | ||
| metadata_auto_snapshot_interval = {{ .Values.garage.metadataAutoSnapshotInterval | quote }} | ||
| {{- end }} | ||
|
|
||
| rpc_bind_addr = "[::]:3901" | ||
| rpc_public_addr = "127.0.0.1:3901" | ||
| rpc_secret = {{ $rpcSecret | quote }} | ||
|
|
||
| {{- if .Values.garage.additionalTopLevelConfig }} | ||
| {{ .Values.garage.additionalTopLevelConfig | nindent 4 }} | ||
| {{- end }} | ||
|
|
||
| [s3_api] | ||
| s3_region = {{ .Values.s3.region | quote }} | ||
| api_bind_addr = "[::]:{{ .Values.service.s3.port }}" | ||
| root_domain = {{ .Values.s3.rootDomain | quote }} | ||
|
|
||
| {{- if .Values.admin.enabled }} | ||
|
|
||
| [admin] | ||
| api_bind_addr = "[::]:{{ .Values.admin.port }}" | ||
| admin_token = {{ $adminToken | quote }} | ||
| {{- end }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could add an additional
helm lintworkflow for the PR CI.https://helm.sh/docs/helm/helm_lint/