Add Argo CD Helm chart release
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
name: Validate and publish metrics-server chart
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: linux_amd64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install Helm
|
||||
run: |
|
||||
curl --fail --silent --show-error --location --output "${RUNNER_TEMP}/helm.tar.gz" https://get.helm.sh/helm-v4.2.2-linux-amd64.tar.gz
|
||||
tar -xzf "${RUNNER_TEMP}/helm.tar.gz" -C "${RUNNER_TEMP}" linux-amd64/helm
|
||||
mv "${RUNNER_TEMP}/linux-amd64/helm" "${RUNNER_TEMP}/helm"
|
||||
chmod 0700 "${RUNNER_TEMP}/helm"
|
||||
echo "${RUNNER_TEMP}" >> "$GITHUB_PATH"
|
||||
- run: ./test.sh
|
||||
|
||||
publish:
|
||||
if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main'
|
||||
needs: validate
|
||||
runs-on: linux_amd64
|
||||
outputs:
|
||||
chart_version: ${{ steps.chart.outputs.version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install Helm
|
||||
run: |
|
||||
curl --fail --silent --show-error --location --output "${RUNNER_TEMP}/helm.tar.gz" https://get.helm.sh/helm-v4.2.2-linux-amd64.tar.gz
|
||||
tar -xzf "${RUNNER_TEMP}/helm.tar.gz" -C "${RUNNER_TEMP}" linux-amd64/helm
|
||||
mv "${RUNNER_TEMP}/linux-amd64/helm" "${RUNNER_TEMP}/helm"
|
||||
chmod 0700 "${RUNNER_TEMP}/helm"
|
||||
echo "${RUNNER_TEMP}" >> "$GITHUB_PATH"
|
||||
- id: chart
|
||||
name: Derive chart version
|
||||
env:
|
||||
COMMIT_SHA: ${{ gitea.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git fetch --tags origin main
|
||||
test "$(git rev-parse origin/main)" = "$COMMIT_SHA"
|
||||
description=$(git describe --tags --long --match 'v[0-9]*' --dirty)
|
||||
case "$description" in *-dirty) echo 'Refusing dirty chart tree' >&2; exit 1 ;; esac
|
||||
version=${description#v}
|
||||
case "$version" in *-0-g*) version=${version%%-0-g*} ;; esac
|
||||
echo "version=${version}" >> "$GITHUB_OUTPUT"
|
||||
- name: Package and publish
|
||||
env:
|
||||
PACKAGE_USERNAME: ${{ secrets.PACKAGE_USERNAME }}
|
||||
PACKAGE_TOKEN: ${{ secrets.PACKAGE_TOKEN }}
|
||||
CHART_VERSION: ${{ steps.chart.outputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test -n "$PACKAGE_USERNAME" && test -n "$PACKAGE_TOKEN"
|
||||
helm lint chart
|
||||
./test.sh
|
||||
helm package chart --version "$CHART_VERSION" --destination "$RUNNER_TEMP"
|
||||
curl --fail --silent --show-error --user "${PACKAGE_USERNAME}:${PACKAGE_TOKEN}" \
|
||||
--request POST --upload-file "${RUNNER_TEMP}/metrics-server-${CHART_VERSION}.tgz" \
|
||||
https://code.brunner.ninja/api/packages/feedc0de/helm/api/charts
|
||||
|
||||
promote:
|
||||
if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main'
|
||||
needs: publish
|
||||
runs-on: linux_amd64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Promote GitOps chart version
|
||||
env:
|
||||
GITOPS_TOKEN: ${{ secrets.GITOPS_TOKEN }}
|
||||
CHART_VERSION: ${{ needs.publish.outputs.chart_version }}
|
||||
COMMIT_SHA: ${{ gitea.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test -n "$GITOPS_TOKEN"
|
||||
git fetch origin main
|
||||
test "$(git rev-parse origin/main)" = "$COMMIT_SHA"
|
||||
auth=$(printf 'feedc0de:%s' "$GITOPS_TOKEN" | base64 | tr -d '\n')
|
||||
git -c "http.extraheader=AUTHORIZATION: basic ${auth}" clone --depth=1 \
|
||||
https://code.brunner.ninja/feedc0de/homelab-gitops.git "$RUNNER_TEMP/gitops"
|
||||
unset auth
|
||||
cd "$RUNNER_TEMP/gitops"
|
||||
gitops_auth=$(printf 'feedc0de:%s' "$GITOPS_TOKEN" | base64 | tr -d '\n')
|
||||
unset GITOPS_TOKEN
|
||||
git config user.name feedc0de
|
||||
git config user.email feedc0de@users.noreply.code.brunner.ninja
|
||||
sed -i -E "0,/targetRevision: [^ ]+/s//targetRevision: ${CHART_VERSION}/" bootstrap/metrics-server.yaml
|
||||
if git diff --quiet; then exit 0; fi
|
||||
git add bootstrap/metrics-server.yaml
|
||||
git commit -m "Deploy metrics-server chart ${CHART_VERSION}"
|
||||
for attempt in 1 2 3; do
|
||||
if git -c "http.extraheader=AUTHORIZATION: basic ${gitops_auth}" push origin HEAD:main; then exit 0; fi
|
||||
git -c "http.extraheader=AUTHORIZATION: basic ${gitops_auth}" fetch origin main
|
||||
git rebase origin/main
|
||||
done
|
||||
exit 1
|
||||
@@ -0,0 +1,21 @@
|
||||
# metrics-server Helm chart
|
||||
|
||||
Homelab-maintained adapter chart for the upstream Kubernetes Metrics Server.
|
||||
It retains the `k8s-app=metrics-server` Deployment and Service selector used by
|
||||
this cluster, while owning the aggregated APIService, RBAC, Service and
|
||||
workload as one release.
|
||||
|
||||
The homelab configuration is managed by Argo CD in
|
||||
[homelab-gitops](https://code.brunner.ninja/feedc0de/homelab-gitops/src/branch/main/apps/metrics-server).
|
||||
The cluster currently requires `kubeletInsecureTls: true`; enable it only on
|
||||
clusters whose kubelet certificates cannot be verified.
|
||||
|
||||
```sh
|
||||
helm repo add brunner.ninja https://brunner.ninja/charts
|
||||
helm repo update brunner.ninja
|
||||
helm upgrade --install metrics-server brunner.ninja/metrics-server \
|
||||
--namespace kube-system --wait --timeout 5m
|
||||
```
|
||||
|
||||
Chart tags version this adapter; `appVersion` records the upstream image
|
||||
version.
|
||||
@@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: metrics-server
|
||||
description: Homelab-maintained adapter chart for Kubernetes Metrics Server
|
||||
type: application
|
||||
version: 0.1.0
|
||||
appVersion: "0.9.0"
|
||||
@@ -0,0 +1,17 @@
|
||||
apiVersion: apiregistration.k8s.io/v1
|
||||
kind: APIService
|
||||
metadata:
|
||||
name: v1beta1.metrics.k8s.io
|
||||
labels:
|
||||
app.kubernetes.io/name: metrics-server
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
spec:
|
||||
group: metrics.k8s.io
|
||||
groupPriorityMinimum: 100
|
||||
insecureSkipTLSVerify: true
|
||||
service:
|
||||
name: metrics-server
|
||||
namespace: {{ .Release.Namespace }}
|
||||
port: 443
|
||||
version: v1beta1
|
||||
versionPriority: 100
|
||||
@@ -0,0 +1,83 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: metrics-server
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
k8s-app: metrics-server
|
||||
app.kubernetes.io/name: metrics-server
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
spec:
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 10
|
||||
strategy:
|
||||
rollingUpdate:
|
||||
maxUnavailable: 0
|
||||
type: RollingUpdate
|
||||
selector:
|
||||
matchLabels:
|
||||
k8s-app: metrics-server
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: metrics-server
|
||||
app.kubernetes.io/name: metrics-server
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
spec:
|
||||
serviceAccountName: metrics-server
|
||||
priorityClassName: system-cluster-critical
|
||||
nodeSelector:
|
||||
kubernetes.io/os: linux
|
||||
containers:
|
||||
- name: metrics-server
|
||||
image: {{ printf "%s:v%s" .Values.image.repository .Chart.AppVersion | quote }}
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
args:
|
||||
- --cert-dir=/tmp
|
||||
- --secure-port=10250
|
||||
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
|
||||
- --kubelet-use-node-status-port
|
||||
- --metric-resolution=15s
|
||||
{{- if .Values.kubeletInsecureTls }}
|
||||
- --kubelet-insecure-tls
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: https
|
||||
containerPort: 10250
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /livez
|
||||
port: https
|
||||
scheme: HTTPS
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 1
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /readyz
|
||||
port: https
|
||||
scheme: HTTPS
|
||||
initialDelaySeconds: 20
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 1
|
||||
failureThreshold: 3
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 200Mi
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop: [ALL]
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
volumeMounts:
|
||||
- name: tmp-dir
|
||||
mountPath: /tmp
|
||||
volumes:
|
||||
- name: tmp-dir
|
||||
emptyDir: {}
|
||||
@@ -0,0 +1,76 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: system:metrics-server-aggregated-reader
|
||||
labels:
|
||||
rbac.authorization.k8s.io/aggregate-to-admin: "true"
|
||||
rbac.authorization.k8s.io/aggregate-to-edit: "true"
|
||||
rbac.authorization.k8s.io/aggregate-to-view: "true"
|
||||
rules:
|
||||
- apiGroups: [metrics.k8s.io]
|
||||
resources: [pods, nodes]
|
||||
verbs: [get, list, watch]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: system:metrics-server
|
||||
labels:
|
||||
app.kubernetes.io/name: metrics-server
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: [nodes/metrics]
|
||||
verbs: [get]
|
||||
- apiGroups: [""]
|
||||
resources: [pods, nodes, namespaces, configmaps]
|
||||
verbs: [get, list, watch]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: metrics-server:system:auth-delegator
|
||||
labels:
|
||||
app.kubernetes.io/name: metrics-server
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:auth-delegator
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: metrics-server
|
||||
namespace: {{ .Release.Namespace }}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: system:metrics-server
|
||||
labels:
|
||||
app.kubernetes.io/name: metrics-server
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:metrics-server
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: metrics-server
|
||||
namespace: {{ .Release.Namespace }}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: metrics-server-auth-reader
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: metrics-server
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: extension-apiserver-authentication-reader
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: metrics-server
|
||||
namespace: {{ .Release.Namespace }}
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: metrics-server
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
k8s-app: metrics-server
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- name: https
|
||||
port: 443
|
||||
protocol: TCP
|
||||
targetPort: https
|
||||
selector:
|
||||
k8s-app: metrics-server
|
||||
@@ -0,0 +1,8 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: metrics-server
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: metrics-server
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"image": {
|
||||
"type": "object",
|
||||
"required": ["repository", "pullPolicy"],
|
||||
"properties": {
|
||||
"repository": {"type": "string", "minLength": 1},
|
||||
"pullPolicy": {"type": "string", "enum": ["Always", "IfNotPresent", "Never"]}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"kubeletInsecureTls": {"type": "boolean"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
image:
|
||||
repository: registry.k8s.io/metrics-server/metrics-server
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
kubeletInsecureTls: false
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
release=${RELEASE_NAME:-metrics-server}
|
||||
namespace=${NAMESPACE:-kube-system}
|
||||
|
||||
helm repo add brunner.ninja https://brunner.ninja/charts --force-update
|
||||
helm repo update brunner.ninja
|
||||
if [ -n "${VALUES_FILE:-}" ]; then
|
||||
helm upgrade --install "$release" brunner.ninja/metrics-server \
|
||||
--namespace "$namespace" --values "$VALUES_FILE" --wait --timeout 5m
|
||||
else
|
||||
helm upgrade --install "$release" brunner.ninja/metrics-server \
|
||||
--namespace "$namespace" --wait --timeout 5m
|
||||
fi
|
||||
@@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
chart_dir=$(CDPATH= cd -- "$(dirname -- "$0")/chart" && pwd)
|
||||
workdir=$(mktemp -d)
|
||||
trap 'rm -rf "$workdir"' EXIT
|
||||
|
||||
helm lint "$chart_dir"
|
||||
helm template metrics-server "$chart_dir" --namespace kube-system \
|
||||
--set kubeletInsecureTls=true >"$workdir/rendered.yaml"
|
||||
grep -q 'kind: APIService' "$workdir/rendered.yaml"
|
||||
grep -q 'name: v1beta1.metrics.k8s.io' "$workdir/rendered.yaml"
|
||||
grep -q 'k8s-app: metrics-server' "$workdir/rendered.yaml"
|
||||
grep -q -- '--kubelet-insecure-tls' "$workdir/rendered.yaml"
|
||||
app_version=$(sed -n 's/^appVersion: *"\{0,1\}\([^" ]*\).*/\1/p' "$chart_dir/Chart.yaml")
|
||||
image_repository=$(sed -n 's/^ repository: *//p' "$chart_dir/values.yaml")
|
||||
grep -Fq "${image_repository}:v${app_version}" "$workdir/rendered.yaml"
|
||||
Reference in New Issue
Block a user