Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,59 @@ jobs:
req-concurrent: "4"
secrets: inherit

# Chained into this run rather than triggered by the tag: release-please tags
# with GITHUB_TOKEN, and a tag pushed that way starts no `on: push: tags`
# workflow. The release is also the only place that already knows the
# version, so nothing has to derive one.
#
# Charts are published on a release only. A branch build is deployed by
# pointing an existing release of the chart at the branch's image tag, which
# needs no chart of its own.
helm:
name: Publish the Helm chart
needs: release-please
if: needs.release-please.outputs.release_created == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v7
with:
ref: ${{ needs.release-please.outputs.tag_name }}

- name: Set up Helm
uses: azure/setup-helm@v5.0.1

# Both versions come from the release rather than from Chart.yaml.
# Release Please keeps that file current anyway, but stating them here
# makes a mismatch impossible instead of something to check for: the
# chart cannot be packaged pointing at an image tag nobody built.
- name: Package
run: helm package charts/vulpes-backend --destination dist --version "${{ needs.release-please.outputs.version }}" --app-version "${{ needs.release-please.outputs.version }}"

- name: Log in to OneLiteFeather Harbor
env:
REGISTRY: ${{ secrets.HARBOR_REGISTRY }}
USERNAME: ${{ secrets.HARBOR_USERNAME }}
PASSWORD: ${{ secrets.HARBOR_PASSWORD }}
run: printf '%s' "$PASSWORD" | helm registry login "$REGISTRY" --username "$USERNAME" --password-stdin

# Into the same Harbor project as the image, under charts/ - the layout
# apus and stelaris-ui already use, so a Flux OCIRepository pointing here
# looks like every other one in the cluster repository.
- name: Push
env:
REGISTRY: ${{ secrets.HARBOR_REGISTRY }}
VERSION: ${{ needs.release-please.outputs.version }}
run: helm push "dist/vulpes-backend-${VERSION}.tgz" "oci://${REGISTRY}/onelitefeather/charts"

- name: Log out
if: always()
env:
REGISTRY: ${{ secrets.HARBOR_REGISTRY }}
run: helm registry logout "$REGISTRY" || true

sbom:
name: Attach SBOM to release
needs: release-please
Expand Down
24 changes: 24 additions & 0 deletions charts/vulpes-backend/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
apiVersion: v2
name: vulpes-backend
description: The Vulpes backend, a Micronaut service deployed to Kubernetes
type: application

# Both lines are kept current by Release Please, which is why the chart version
# and the app version move together: the chart only ever describes the image
# built from the same commit, so a separate chart version would be a second
# number to reason about with nothing extra to say. The cluster repository's
# OCIRepository pins the chart, and image.tag is left empty so it falls back to
# this appVersion -- pinning the chart therefore pins the image.
version: 2.5.0 # x-release-please-version
appVersion: "2.5.0" # x-release-please-version

home: https://github.com/OneLiteFeatherNET/Vulpes-Backend
sources:
- https://github.com/OneLiteFeatherNET/Vulpes-Backend
maintainers:
- name: OneLiteFeatherNET
email: contact@onelitefeather.net
url: https://onelitefeather.net
keywords:
- vulpes
- micronaut
22 changes: 22 additions & 0 deletions charts/vulpes-backend/templates/NOTES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
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 "micronaut.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 its status by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "micronaut.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "micronaut.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 "micronaut.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 }}
73 changes: 73 additions & 0 deletions charts/vulpes-backend/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{{/*
The helpers below are still named `micronaut.*`, not `vulpes-backend.*`, and
that is deliberate: this chart is a fork of helm/micronaut in the cluster
repository, and keeping the templates byte-identical is what lets a later fix
over there be carried across with `diff -r`. Renaming them would touch every
template file and turn that diff into noise.

The rendered resource names are unaffected either way -- they come from
.Chart.Name/.Release.Name, and the cluster overlays pin them with
nameOverride/fullnameOverride so the Deployment's immutable selector survives
this chart move.
*/}}

{{/*
Expand the name of the chart.
*/}}
{{- define "micronaut.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 "micronaut.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- if contains .Chart.Name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}

{{/*
Chart name and version
*/}}
{{- define "micronaut.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" }}
{{- end }}

{{/*
Common labels
*/}}
{{- define "micronaut.labels" -}}
app.kubernetes.io/name: {{ include "micronaut.name" . }}
helm.sh/chart: {{ include "micronaut.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}


{{/*
Create the name of the service account to use
*/}}
{{- define "micronaut.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "micronaut.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "micronaut.selectorLabels" -}}
app.kubernetes.io/name: {{ include "micronaut.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
13 changes: 13 additions & 0 deletions charts/vulpes-backend/templates/configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "micronaut.fullname" . }}-config
labels:
{{- include "micronaut.labels" . | nindent 4 }}
data:
application.yml: |-
{{- nindent 4 .Values.config.base }}
{{- range $p, $content := .Values.config.profiles }}
application-{{ $p }}.yml: |-
{{- nindent 4 $content }}
{{- end }}
170 changes: 170 additions & 0 deletions charts/vulpes-backend/templates/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "micronaut.fullname" . }}
labels:
{{- include "micronaut.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
{{- with .Values.strategy }}
strategy:
{{- toYaml . | nindent 4 }}
{{- end }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "micronaut.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "micronaut.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- with .Values.podLabels }}
{{ toYaml . | indent 8 }}
{{- end }}
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
{{- if and .Values.secrets.enabled .Values.secrets.files }}
checksum/secrets: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
{{- end }}
{{- with .Values.podAnnotations }}
{{ toYaml . | indent 8 }}
{{- end }}
spec:
{{- if .Values.imagePullSecrets }}
imagePullSecrets:
{{ toYaml .Values.imagePullSecrets | indent 8 }}
{{- end }}
serviceAccountName: {{- if .Values.serviceAccount.create }} {{ include "micronaut.fullname" . }} {{- else }} {{ default "default" .Values.serviceAccount.name }} {{- end }}
{{- if .Values.securityContext.enabled }}
securityContext:
{{- omit .Values.securityContext "enabled" | toYaml | nindent 8 }}
{{- end }}
{{- with .Values.priorityClassName }}
priorityClassName: {{ . }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if .Values.tracing.enabled }}
initContainers:
- name: otel-agent
image: {{ .Values.tracing.image }}
command: ["cp", "/javaagent.jar", "/otel/javaagent.jar"]
{{- if .Values.containerSecurityContext.enabled }}
securityContext:
{{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }}
{{- end }}
resources:
{{- toYaml .Values.tracing.resources | nindent 12 }}
volumeMounts:
- name: otel-agent
mountPath: /otel
{{- end }}
containers:
- name: app
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- if .Values.containerSecurityContext.enabled }}
securityContext:
{{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }}
{{- end }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
env:
- name: MICRONAUT_ENVIRONMENTS
value: "{{ join "," .Values.profiles }}"
- name: MICRONAUT_CONFIG_FILES
value: "/config/application.yml{{- range $p := .Values.profiles }},/config/application-{{$p}}.yml{{- end }}{{- if and .Values.secrets.enabled .Values.secrets.files }}{{- range $p := .Values.profiles }},/secrets/application-{{$p}}.yml{{- end }}{{- end }}"
{{- range .Values.env }}
- name: {{ .name }}
value: {{ .value | quote }}
{{- end }}
{{- if .Values.tracing.enabled }}
# JDK_JAVA_OPTIONS is independent of JAVA_TOOL_OPTIONS (which
# apps commonly override wholesale via .Values.env above), so
# the agent attaches regardless of what that list contains.
- name: JDK_JAVA_OPTIONS
value: "-javaagent:/otel/javaagent.jar"
- name: OTEL_SERVICE_NAME
value: {{ .Release.Name | quote }}
- name: OTEL_TRACES_EXPORTER
value: "otlp"
- name: OTEL_METRICS_EXPORTER
value: "none"
- name: OTEL_LOGS_EXPORTER
value: "none"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: {{ .Values.tracing.endpoint | quote }}
- name: OTEL_EXPORTER_OTLP_PROTOCOL
value: "grpc"
{{- end }}
{{- if .Values.envFrom }}
envFrom:
{{ toYaml .Values.envFrom | indent 12 }}
{{- end }}
{{- if .Values.startupProbe.enabled }}
startupProbe:
httpGet:
path: {{ .Values.readinessProbe.path }}
port: http
failureThreshold: {{ .Values.startupProbe.failureThreshold }}
periodSeconds: {{ .Values.startupProbe.periodSeconds }}
{{- end }}
readinessProbe:
httpGet:
path: {{ .Values.readinessProbe.path }}
port: http
initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.readinessProbe.periodSeconds }}
livenessProbe:
httpGet:
path: {{ .Values.livenessProbe.path }}
port: http
initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.livenessProbe.periodSeconds }}
resources:
{{ toYaml .Values.resources | indent 12 }}
volumeMounts:
# The root filesystem is read-only; the JVM still wants a scratch
# dir (java.io.tmpdir, Netty, JIT dumps).
- name: tmp
mountPath: /tmp
- name: config
mountPath: /config
{{- if and .Values.secrets.enabled .Values.secrets.files }}
- name: secrets
mountPath: /secrets
{{- end }}
{{- if .Values.tracing.enabled }}
- name: otel-agent
mountPath: /otel
readOnly: true
{{- end }}
volumes:
- name: tmp
emptyDir: {}
- name: config
configMap:
name: {{ include "micronaut.fullname" . }}-config
{{- if and .Values.secrets.enabled .Values.secrets.files }}
- name: secrets
secret:
secretName: {{ include "micronaut.fullname" . }}-secrets
{{- end }}
{{- if .Values.tracing.enabled }}
- name: otel-agent
emptyDir: {}
{{- end }}
Loading
Loading