Skip to content

Commit

Permalink
feat(helm): add chart
Browse files Browse the repository at this point in the history
  • Loading branch information
hairmare committed Feb 2, 2024
1 parent 7fada0f commit 5b40cce
Show file tree
Hide file tree
Showing 20 changed files with 754 additions and 9 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ __pycache__
.Python
.python-version
*.swp
*.trivy
121 changes: 112 additions & 9 deletions .github/workflows/ghcr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,29 +34,73 @@ jobs:
steps:
- uses: actions/checkout@v4

- uses: sigstore/[email protected]

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to the Container registry
uses: docker/login-action@v3
if: github.event_name != 'pull_request'
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=dev,enable={{is_default_branch}}
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
- name: Build and push
- name: Build
id: docker
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: false
load: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Run Trivy vulnerability scanner
uses: aquasecurity/[email protected]
with:
image-ref: '${{ steps.docker.output. }}:${{ steps.meta.outputs.version }}'
format: 'json'
ignore-unfixed: true
list-all-pkgs: true
output: 'trivy.json'

- name: Convert trivy results to sarif
uses: aquasecurity/[email protected]
with:
image-ref: trivy.json
scan-type: 'convert'
format: 'sarif'
# we don't actually limit them, but this gates the convert action
limit-severities-for-sarif: true
# empty makes it skip the --vuln-type arg
vuln-type: ''
output: 'trivy.sarif'

- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: 'trivy.sarif'

- name: Log in to the Container registry
uses: docker/login-action@v3
if: github.event_name != 'pull_request'
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Push
id: docker_push
uses: docker/build-push-action@v5
with:
context: .
Expand All @@ -66,3 +110,62 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Sign the images with GitHub OIDC Token using cosign
run: cosign sign --yes ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.docker_push.outputs.digest }}
if: github.event_name != 'pull_request' && startsWith(github.event.ref, 'refs/tags/v')
env:
TAGS: ${{ steps.meta.outputs.tags }}

- name: Convert trivy results to CycloneDX
uses: aquasecurity/[email protected]
with:
image-ref: trivy.json
scan-type: 'convert'
format: 'cyclonedx'
# we don't actually limit them, but this gates the convert action
limit-severities-for-sarif: true
# empty makes it skip the --vuln-type arg
vuln-type: ''
output: 'trivy.cdx'

- name: Attach an SBOM attestation to the signed image
run: cosign attest --yes --type cyclonedx --predicate trivy.cdx ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.docker_push.outputs.digest }}
if: github.event_name != 'pull_request' && startsWith(github.event.ref, 'refs/tags/v')

- name: Set up Helm
uses: azure/[email protected]
with:
version: v3.14.0

- uses: actions/setup-python@v4
with:
python-version: '3.x'

- name: Install chart-testing
uses: helm/[email protected]

- name: Run chart-testing (lint)
run: ct --config=deploy/ct.yaml lint

- name: Create kind cluster
uses: helm/[email protected]

- name: Load dev image into K8s
run: |
docker export -o /tmp/img.tar ghcr.io/projectcaluma/alexandria:dev
kind load image-archive /tmp/img.tar --name chart-testing
- name: Run chart-testing (install)
run: ct --config=deploy/ct.yaml install

- name: Prepare Chart Metadata
id: chartmeta
run: echo version=${GITHUB_REF#refs/tags/v} >> $GITHUB_OUTPUT

- name: Package Chart
run: helm package --version ${{ steps.chartmeta.outputs.version }} --app-version ${{ steps.chartmeta.outputs.version }} --destination=dist deploy/charts/alexandria

- name: Push Chart
run: helm push dist/*.tgz oci://ghcr.io/projectcaluma/helm
if: github.event_name != 'pull_request' && startsWith(github.event.ref, 'refs/tags/v')
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,6 @@ target/

# Editor swap files
*.swp

# Helm
deploy/charts/*/charts/
10 changes: 10 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,13 @@ poetry install
poetry run pre-commit install --hook=pre-commit
poetry run pre-commit install --hook=commit-msg
```

### Helm Chart testing

You can spin up an instance of alexandria locally with ease. Out of the box this deploys the
latest `dev` tag from ghcr.io.

```bash
helm dep build deploy/charts/alexandria
helm install alexandria -f deploy/charts/alexandria/ci/default-values.yaml alexandria deploy/charts/alexandria
```
23 changes: 23 additions & 0 deletions deploy/charts/alexandria/.helmignore
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/
9 changes: 9 additions & 0 deletions deploy/charts/alexandria/Chart.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
dependencies:
- name: postgresql
repository: https://charts.bitnami.com/bitnami
version: 14.0.0
- name: minio
repository: https://charts.min.io
version: 5.0.15
digest: sha256:2474e6946f37fc6c60a744f92b3b336cee90ccf9c8dfcdf56bfb425904fe132a
generated: "2024-02-03T00:24:29.412659335+01:00"
24 changes: 24 additions & 0 deletions deploy/charts/alexandria/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
apiVersion: v2
name: alexandria
description: Deploy the Alexandria DMS to Kubernetes
type: application
# managed by CD
version: 0.0.0
appVersion: dev
home: https://caluma.io/
icon: https://avatars0.githubusercontent.com/u/42172011?s=200&v=4
sources:
- https://github.com/projectcaluma/emeis
- https://github.com/projectcaluma/charts
maintainers:
- name: projectcaluma
email: [email protected]
dependencies:
- name: postgresql
version: ~14.0.0
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
- name: minio
version: ~5.0.15
repository: https://charts.min.io
condition: minio.enabled
14 changes: 14 additions & 0 deletions deploy/charts/alexandria/ci/default-values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
postgresql:
auth:
password: alexandria
primary:
persistence:
enabled: false
minio:
accessKey: alexandria
secretKey: alexandria
persistence:
enabled: false
resources:
requests:
memory: 512Mi
25 changes: 25 additions & 0 deletions deploy/charts/alexandria/templates/NOTES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{{- if or .Values.postgresql.enabled .Values.minio.enabled }}
PRODUCTION WARNING: Either embedded PostgreSQL or MinIO instance is configured! This configuration is not supported for production use!
{{- end }}
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
{{- end }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "alexandria.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "alexandria.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "alexandria.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "alexandria.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}
62 changes: 62 additions & 0 deletions deploy/charts/alexandria/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "alexandria.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "alexandria.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 }}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "alexandria.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Common labels
*/}}
{{- define "alexandria.labels" -}}
helm.sh/chart: {{ include "alexandria.chart" . }}
{{ include "alexandria.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "alexandria.selectorLabels" -}}
app.kubernetes.io/name: {{ include "alexandria.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{/*
Create the name of the service account to use
*/}}
{{- define "alexandria.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "alexandria.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
43 changes: 43 additions & 0 deletions deploy/charts/alexandria/templates/configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "alexandria.fullname" . }}
labels:
{{- include "alexandria.labels" . | nindent 4 }}
data:
# Django
ENV: {{ .Values.alexandria.env | quote }}
ALLOWED_HOSTS: {{ .Values.alexandria.allowedHosts | quote }}
ALLOW_ANONYMOUS_WRITE: {{ .Values.alexandria.allowAnonymousWrite | quote }}
LANGUAGES: {{ join "," .Values.alexandria.languages | quote}}
VISIBILITY_CLASSES: {{ .Values.alexandria.visibilityClasses | quote }}
PERMISSION_CLASSES: {{ .Values.alexandria.permissionClasses | quote }}
# DB
{{- if .Values.postgresql.enabled }}
DATABASE_USER: {{ .Values.postgresql.auth.username | quote }}
DATABASE_NAME: {{ .Values.postgresql.auth.database | quote }}
{{- else }}
DATABASE_USER: {{ .Values.alexandria.postgresql.username | quote }}
DATABASE_NAME: {{ .Values.alexandria.postgresql.database | quote }}
{{- end }}
{{- if and .Values.postgresql.enabled .Values.alexandria.postgresql.existingHost }}
{{ fail "postgresql.enabled and alexandria.postgresql.existingHost are mutually exclusive, please pick one" }}
{{- end }}
{{- if .Values.postgresql.enabled }}
DATABASE_HOST: {{ template "alexandria.fullname" . }}-postgresql
{{- else if .Values.alexandria.postgresql.existingHost }}
DATABASE_HOST: {{ .Values.alexandria.postgresql.existingHost | quote }}
{{- else }}
{{ fail "neither postgresql.enabled or alexandria.postgresql.existingHost are set, please pick one" }}
{{- end }}
# S3
{{- if and .Values.minio.enabled .Values.alexandria.s3.existingHost }}
{{ fail "minio.enabled and alexandria.s3.existingUrl are mutually exclusive, please pick one" }}
{{- end }}
{{- if .Values.minio.enabled }}
ALEXANDRIA_S3_ENDPOINT_URL: http://{{ template "alexandria.fullname" . }}-minio:9000
{{- else if .Values.alexandria.s3.existingUrl }}
ALEXANDRIA_S3_ENDPOINT_URL: {{ .Values.alexandria.s3.existingUrl | quote }}
{{- else }}
{{ fail "neither minio.enabled or alexandria.s3.existingHost are set, please pick one" }}
{{- end }}
Loading

0 comments on commit 5b40cce

Please sign in to comment.