diff --git a/.github/workflows/chart-preview.yml b/.github/workflows/chart-preview.yml index 3276a4c..e39f14b 100644 --- a/.github/workflows/chart-preview.yml +++ b/.github/workflows/chart-preview.yml @@ -39,7 +39,7 @@ jobs: - name: Package and push run: | helm package ./chart - helm push theia-shared-cache-${PREVIEW_VERSION}.tgz oci://ghcr.io/${{ env.REGISTRY_OWNER }}/charts + helm push eduide-shared-cache-${PREVIEW_VERSION}.tgz oci://ghcr.io/${{ env.REGISTRY_OWNER }}/charts - name: Comment install instructions uses: marocchino/sticky-pull-request-comment@v2 @@ -49,7 +49,7 @@ jobs: ## Chart Preview Ready ```bash - helm install test oci://ghcr.io/${{ env.REGISTRY_OWNER }}/charts/theia-shared-cache --version ${{ env.PREVIEW_VERSION }} + helm install test oci://ghcr.io/${{ env.REGISTRY_OWNER }}/charts/eduide-shared-cache --version ${{ env.PREVIEW_VERSION }} ``` _Updated: ${{ github.sha }}_ diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index c8c1b16..7a87004 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -4,16 +4,16 @@ on: push: branches: - main - tags: - - 'v*' paths: - 'src/**' + - 'chart/**' - '.github/workflows/docker-build.yml' pull_request: branches: - main paths: - 'src/**' + - 'chart/**' env: REGISTRY: ghcr.io @@ -34,13 +34,22 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Log in to Container Registry - if: github.event_name != 'pull_request' uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Compute version tag + id: version + run: | + BASE=$(grep '^version:' chart/Chart.yaml | awk '{print $2}') + if [[ "${{ github.event_name }}" == "pull_request" ]]; then + echo "tag=${BASE}-pr.${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT + else + echo "tag=${BASE}" >> $GITHUB_OUTPUT + fi + - name: Extract metadata (tags, labels) id: meta uses: docker/metadata-action@v5 @@ -49,15 +58,14 @@ jobs: tags: | type=ref,event=branch type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} type=sha,prefix= + type=raw,value=${{ steps.version.outputs.tag }} - name: Build and push Docker image uses: docker/build-push-action@v5 with: context: ./src - push: ${{ github.event_name != 'pull_request' }} + push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d931e17..aeffad2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -41,12 +41,12 @@ jobs: - name: Package and push to OCI run: | helm package ./chart - helm push theia-shared-cache-${{ steps.chart.outputs.version }}.tgz oci://ghcr.io/${{ env.REGISTRY_OWNER }}/charts + helm push eduide-shared-cache-${{ steps.chart.outputs.version }}.tgz oci://ghcr.io/${{ env.REGISTRY_OWNER }}/charts - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: - tag_name: theia-shared-cache-${{ steps.chart.outputs.version }} - name: theia-shared-cache ${{ steps.chart.outputs.version }} + tag_name: eduide-shared-cache-${{ steps.chart.outputs.version }} + name: eduide-shared-cache ${{ steps.chart.outputs.version }} generate_release_notes: true diff --git a/chart/Chart.yaml b/chart/Chart.yaml index ca61ec8..50792df 100644 --- a/chart/Chart.yaml +++ b/chart/Chart.yaml @@ -1,14 +1,11 @@ apiVersion: v2 -name: theia-shared-cache -description: A Gradle Build Cache server with Redis backend for Theia IDE deployments in Kubernetes +name: eduide-shared-cache +description: A Gradle Build Cache server with Redis backend for EduIDE deployments in Kubernetes type: application -# Chart version - bump for breaking changes -version: 0.4.0 - -# Application version - matches the cache server version -appVersion: "0.1.0" +# Bump this version on every release — also used as the Docker image tag +version: 0.5.0 dependencies: - name: reposilite diff --git a/chart/templates/_helpers.tpl b/chart/templates/_helpers.tpl index d2bcf97..c15c2c8 100644 --- a/chart/templates/_helpers.tpl +++ b/chart/templates/_helpers.tpl @@ -1,15 +1,14 @@ {{/* -Standard labels for theia-shared-cache resources +Standard labels for eduide-shared-cache resources */}} -{{- define "theia-shared-cache.labels" -}} +{{- define "eduide-shared-cache.labels" -}} app.kubernetes.io/name: {{ .Chart.Name }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/managed-by: {{ .Release.Service }} helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} {{- end -}} -{{- define "theia-shared-cache.selectorLabels" -}} +{{- define "eduide-shared-cache.selectorLabels" -}} app.kubernetes.io/name: {{ .Chart.Name }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end -}} diff --git a/chart/templates/auth-secrets.yaml b/chart/templates/auth-secrets.yaml index 9291768..4e74a30 100644 --- a/chart/templates/auth-secrets.yaml +++ b/chart/templates/auth-secrets.yaml @@ -1,11 +1,10 @@ {{- if and .Values.enabled .Values.auth.enabled }} -# Read-only cache credentials (for Theia IDE / students) +# Read-only cache credentials (for EduIDE / students) apiVersion: v1 kind: Secret metadata: - name: {{ .Release.Name }}-cache-reader + name: {{ .Chart.Name }}-reader labels: - app: {{ .Release.Name }} role: reader type: kubernetes.io/basic-auth data: @@ -16,9 +15,8 @@ data: apiVersion: v1 kind: Secret metadata: - name: {{ .Release.Name }}-cache-writer + name: {{ .Chart.Name }}-writer labels: - app: {{ .Release.Name }} role: writer type: kubernetes.io/basic-auth data: diff --git a/chart/templates/configmap.yaml b/chart/templates/configmap.yaml index 2ca153b..184f04b 100644 --- a/chart/templates/configmap.yaml +++ b/chart/templates/configmap.yaml @@ -2,9 +2,9 @@ apiVersion: v1 kind: ConfigMap metadata: - name: {{ .Release.Name }}-config + name: {{ .Chart.Name }}-config labels: - {{- include "theia-shared-cache.labels" . | nindent 4 }} + {{- include "eduide-shared-cache.labels" . | nindent 4 }} app.kubernetes.io/component: cache-server data: config.yaml: | @@ -20,7 +20,7 @@ data: {{- end}} storage: - addr: "{{ .Release.Name }}-redis:6379" + addr: "{{ .Chart.Name }}-redis:6379" cache: max_entry_size_mb: {{ .Values.cache.maxEntrySizeMB }} diff --git a/chart/templates/deployment.yaml b/chart/templates/deployment.yaml index 6a84588..591f95d 100644 --- a/chart/templates/deployment.yaml +++ b/chart/templates/deployment.yaml @@ -2,20 +2,20 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: {{ .Release.Name }}-cache-server + name: {{ .Chart.Name }}-server labels: - {{- include "theia-shared-cache.labels" . | nindent 4 }} + {{- include "eduide-shared-cache.labels" . | nindent 4 }} app.kubernetes.io/component: cache-server spec: replicas: 1 selector: matchLabels: - {{- include "theia-shared-cache.selectorLabels" . | nindent 6 }} + {{- include "eduide-shared-cache.selectorLabels" . | nindent 6 }} app.kubernetes.io/component: cache-server template: metadata: labels: - {{- include "theia-shared-cache.selectorLabels" . | nindent 8 }} + {{- include "eduide-shared-cache.selectorLabels" . | nindent 8 }} app.kubernetes.io/component: cache-server annotations: checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} @@ -29,15 +29,14 @@ spec: - -c - | echo "Waiting for Redis to be ready..." - until nc -z {{ .Release.Name }}-redis 6379; do + until nc -z {{ .Chart.Name }}-redis 6379; do echo "Redis not ready, waiting..." sleep 2 done echo "Redis is ready!" containers: - name: cache-server - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.Version }}" ports: - name: http containerPort: 8080 @@ -46,25 +45,25 @@ spec: - name: REDIS_PASSWORD valueFrom: secretKeyRef: - name: {{ .Release.Name }}-redis-secret + name: {{ .Chart.Name }}-redis-secret key: redis-password {{- if .Values.auth.enabled }} - name: CACHE_READER_PASSWORD valueFrom: secretKeyRef: - name: {{ .Release.Name }}-cache-reader + name: {{ .Chart.Name }}-reader key: password - name: CACHE_WRITER_PASSWORD valueFrom: secretKeyRef: - name: {{ .Release.Name }}-cache-writer + name: {{ .Chart.Name }}-writer key: password {{- end }} args: - --config - /etc/gradle-cache/config.yaml resources: - {{- toYaml .Values.resources.cacheServer | nindent 12 }} + {{- toYaml .Values.resources | nindent 12 }} livenessProbe: httpGet: path: /ping @@ -99,7 +98,7 @@ spec: volumes: - name: config configMap: - name: {{ .Release.Name }}-config + name: {{ .Chart.Name }}-config {{- if .Values.tls.enabled }} - name: tls-certs secret: diff --git a/chart/templates/redis-deployment.yaml b/chart/templates/redis-deployment.yaml index be8009a..01fd218 100644 --- a/chart/templates/redis-deployment.yaml +++ b/chart/templates/redis-deployment.yaml @@ -2,20 +2,20 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: {{ .Release.Name }}-redis + name: {{ .Chart.Name }}-redis labels: - {{- include "theia-shared-cache.labels" . | nindent 4 }} + {{- include "eduide-shared-cache.labels" . | nindent 4 }} app.kubernetes.io/component: storage spec: replicas: 1 selector: matchLabels: - {{- include "theia-shared-cache.selectorLabels" . | nindent 6 }} + {{- include "eduide-shared-cache.selectorLabels" . | nindent 6 }} app.kubernetes.io/component: storage template: metadata: labels: - {{- include "theia-shared-cache.selectorLabels" . | nindent 8 }} + {{- include "eduide-shared-cache.selectorLabels" . | nindent 8 }} app.kubernetes.io/component: storage spec: containers: @@ -37,10 +37,10 @@ spec: - name: REDIS_PASSWORD valueFrom: secretKeyRef: - name: {{ .Release.Name }}-redis-secret + name: {{ .Chart.Name }}-redis-secret key: redis-password resources: - {{- toYaml .Values.resources.redis | nindent 12 }} + {{- toYaml .Values.redis.resources | nindent 12 }} livenessProbe: exec: command: @@ -72,7 +72,7 @@ spec: - name: REDIS_PASSWORD valueFrom: secretKeyRef: - name: {{ .Release.Name }}-redis-secret + name: {{ .Chart.Name }}-redis-secret key: redis-password resources: requests: diff --git a/chart/templates/redis-secret.yaml b/chart/templates/redis-secret.yaml index 7b62ecd..1140cd7 100644 --- a/chart/templates/redis-secret.yaml +++ b/chart/templates/redis-secret.yaml @@ -2,13 +2,13 @@ apiVersion: v1 kind: Secret metadata: - name: {{ .Release.Name }}-redis-secret + name: {{ .Chart.Name }}-redis-secret labels: - {{- include "theia-shared-cache.labels" . | nindent 4 }} + {{- include "eduide-shared-cache.labels" . | nindent 4 }} app.kubernetes.io/component: storage type: Opaque data: - {{- $secretName := printf "%s-redis-secret" .Release.Name }} + {{- $secretName := printf "%s-redis-secret" .Chart.Name }} {{- $existingSecret := lookup "v1" "Secret" .Release.Namespace $secretName }} {{- if $existingSecret }} redis-password: {{ index $existingSecret.data "redis-password" }} diff --git a/chart/templates/redis-service.yaml b/chart/templates/redis-service.yaml index b4bc20a..2cee120 100644 --- a/chart/templates/redis-service.yaml +++ b/chart/templates/redis-service.yaml @@ -2,9 +2,9 @@ apiVersion: v1 kind: Service metadata: - name: {{ .Release.Name }}-redis + name: {{ .Chart.Name }}-redis labels: - {{- include "theia-shared-cache.labels" . | nindent 4 }} + {{- include "eduide-shared-cache.labels" . | nindent 4 }} app.kubernetes.io/component: storage spec: type: ClusterIP @@ -18,6 +18,6 @@ spec: targetPort: metrics protocol: TCP selector: - {{- include "theia-shared-cache.selectorLabels" . | nindent 4 }} + {{- include "eduide-shared-cache.selectorLabels" . | nindent 4 }} app.kubernetes.io/component: storage {{- end }} diff --git a/chart/templates/reposilite-secret.yaml b/chart/templates/reposilite-secret.yaml new file mode 100644 index 0000000..90ad0c1 --- /dev/null +++ b/chart/templates/reposilite-secret.yaml @@ -0,0 +1,21 @@ +{{- if .Values.reposilite.enabled }} +{{- $secretName := printf "%s-reposilite-secrets" .Chart.Name }} +{{- $existingSecret := lookup "v1" "Secret" .Release.Namespace $secretName }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }} + labels: + {{- include "eduide-shared-cache.labels" . | nindent 4 }} + app.kubernetes.io/component: dependency-cache +type: Opaque +data: + adminToken: {{ printf "admin:%s" .Values.reposilite.adminToken | b64enc | quote }} + {{- if $existingSecret }} + prometheusUsername: {{ index $existingSecret.data "prometheusUsername" }} + prometheusPassword: {{ index $existingSecret.data "prometheusPassword" }} + {{- else }} + prometheusUsername: {{ "prometheus" | b64enc | quote }} + prometheusPassword: {{ randAlphaNum 32 | b64enc | quote }} + {{- end }} +{{- end }} diff --git a/chart/templates/reposilite-shared-config.yaml b/chart/templates/reposilite-shared-config.yaml index 7fcee37..fb4e494 100644 --- a/chart/templates/reposilite-shared-config.yaml +++ b/chart/templates/reposilite-shared-config.yaml @@ -4,7 +4,7 @@ kind: ConfigMap metadata: name: reposilite-shared-config labels: - {{- include "theia-shared-cache.labels" . | nindent 4 }} + {{- include "eduide-shared-cache.labels" . | nindent 4 }} app.kubernetes.io/component: dependency-cache data: configuration.shared.json: | @@ -17,18 +17,15 @@ data: "redeployment": false, "preserveSnapshots": false, "proxied": [ + {{- range $i, $url := .Values.reposilite.proxyRepositories }} + {{- if $i }},{{ end }} { - "reference": "https://repo1.maven.org/maven2", - "store": true, - "allowedGroups": [], - "allowedExtensions": [".jar", ".war", ".xml", ".pom", ".module", ".asc", ".md5", ".sha1", ".sha256", ".sha512"] - }, - { - "reference": "https://plugins.gradle.org/m2", + "reference": "{{ $url }}", "store": true, "allowedGroups": [], "allowedExtensions": [".jar", ".war", ".xml", ".pom", ".module", ".asc", ".md5", ".sha1", ".sha256", ".sha512"] } + {{- end }} ] } ] diff --git a/chart/templates/service.yaml b/chart/templates/service.yaml index 2235bb2..8ac1a77 100644 --- a/chart/templates/service.yaml +++ b/chart/templates/service.yaml @@ -2,9 +2,9 @@ apiVersion: v1 kind: Service metadata: - name: {{ .Release.Name }}-cache + name: {{ .Chart.Name }} labels: - {{- include "theia-shared-cache.labels" . | nindent 4 }} + {{- include "eduide-shared-cache.labels" . | nindent 4 }} app.kubernetes.io/component: cache-server spec: type: ClusterIP @@ -14,6 +14,6 @@ spec: targetPort: http protocol: TCP selector: - {{- include "theia-shared-cache.selectorLabels" . | nindent 4 }} + {{- include "eduide-shared-cache.selectorLabels" . | nindent 4 }} app.kubernetes.io/component: cache-server {{- end }} diff --git a/chart/templates/servicemonitor-cache.yaml b/chart/templates/servicemonitor-cache.yaml new file mode 100644 index 0000000..be479de --- /dev/null +++ b/chart/templates/servicemonitor-cache.yaml @@ -0,0 +1,23 @@ +{{- if and .Values.enabled .Values.monitoring.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ .Chart.Name }}-cache + namespace: {{ .Release.Namespace }} + labels: + {{- include "eduide-shared-cache.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + app.kubernetes.io/component: cache-server + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} + endpoints: + - port: http + path: /metrics + interval: 15s + scheme: https + tlsConfig: + insecureSkipVerify: true +{{- end }} diff --git a/chart/templates/servicemonitor-reposilite.yaml b/chart/templates/servicemonitor-reposilite.yaml new file mode 100644 index 0000000..ca86a98 --- /dev/null +++ b/chart/templates/servicemonitor-reposilite.yaml @@ -0,0 +1,27 @@ +{{- if and .Values.reposilite.enabled .Values.monitoring.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ .Chart.Name }}-reposilite + namespace: {{ .Release.Namespace }} + labels: + {{- include "eduide-shared-cache.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + app.kubernetes.io/name: reposilite + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} + endpoints: + - port: http + path: /metrics + interval: 15s + basicAuth: + username: + name: {{ .Chart.Name }}-reposilite-secrets + key: prometheusUsername + password: + name: {{ .Chart.Name }}-reposilite-secrets + key: prometheusPassword +{{- end }} diff --git a/chart/values.yaml b/chart/values.yaml index 0269a8a..bd4bd59 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -1,48 +1,26 @@ -# Gradle Build Cache Configuration +# ============================================================ +# Cache Server +# ============================================================ -# Enable/disable the entire cache deployment enabled: true -# Docker image image: - repository: ghcr.io/ls1intum/theia-shared-cache/gradle-cache - tag: "main" - pullPolicy: IfNotPresent + repository: ghcr.io/eduide/eduide-shared-cache/gradle-cache + # Default is the Chart Version + tag: "" # Authentication credentials (role-based) -# reader: read-only access (for Theia IDE / students) +# reader: read-only access (for EduIDE / students) # writer: read-write access (for CI/CD, admin, cache pre-warming) auth: enabled: true reader: username: "reader" - # IMPORTANT: Change this password in production! - # optimaly from github envrionment secrets password: "changeme-reader" writer: username: "writer" - # IMPORTANT: Change this password in production! - # optimaly from github envrionment secrets password: "changeme-writer" -# Resource limits -resources: - cacheServer: - requests: - memory: "256Mi" - cpu: "100m" - limits: - memory: "1Gi" - cpu: "500m" - - redis: - requests: - memory: "128Mi" - cpu: "100m" - limits: - memory: "2Gi" - cpu: "1000m" - # Cache settings (shared by Gradle and Bazel) cache: # Maximum entry size in MB @@ -51,22 +29,65 @@ cache: verifyCASHash: true tls: - # Enable TLS for the cache server enabled: false - # TLS secret name (if TLS is enabled) + # Kubernetes secret name containing tls.crt and tls.key secretName: "" -# Reposilite - Maven/Gradle dependency proxy +resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "500m" + +# ============================================================ +# Redis (internal storage backend) +# ============================================================ + +redis: + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "2Gi" + cpu: "1000m" + +# ============================================================ +# Monitoring +# ============================================================ + +monitoring: + # Create ServiceMonitor resources for Prometheus discovery. + # Requires the Prometheus Operator to be installed in the cluster. + enabled: true + +# ============================================================ +# Reposilite (Maven/Gradle dependency proxy) +# ============================================================ + reposilite: enabled: true - # Persistence for cached artifacts (stored on Ceph) + # Admin access token for Reposilite management + adminToken: "changeme" + + # Prometheus credentials are auto-generated on first install and stored in: + # eduide-shared-cache-reposilite-secrets (keys: prometheusUsername, prometheusPassword) + + # Maven/Gradle repositories to proxy and cache + proxyRepositories: + - "https://repo1.maven.org/maven2" + - "https://plugins.gradle.org/m2" + + # Persistence for cached artifacts persistence: enabled: true size: 20Gi storageClass: "csi-rbd-sc" - # Resource limits (Reposilite is lightweight - ~20-30MB RAM) + # Resource limits resources: requests: memory: "128Mi" @@ -75,18 +96,30 @@ reposilite: memory: "256Mi" cpu: "500m" - # JVM and Reposilite startup settings + # ------------------------------------------------------------ + # Internal: subchart wiring — not intended for user modification + # ------------------------------------------------------------ env: - name: JAVA_OPTS value: "-Xmx128M" + - name: REPOSILITE_ADMIN_TOKEN + valueFrom: + secretKeyRef: + name: eduide-shared-cache-reposilite-secrets + key: adminToken - name: REPOSILITE_OPTS - value: "--token admin:changeme --shared-configuration=/etc/reposilite/configuration.shared.json" + value: "--token $(REPOSILITE_ADMIN_TOKEN) --shared-configuration=/etc/reposilite/configuration.shared.json" - name: REPOSILITE_PROMETHEUS_USER - value: "prometheus" + valueFrom: + secretKeyRef: + name: eduide-shared-cache-reposilite-secrets + key: prometheusUsername - name: REPOSILITE_PROMETHEUS_PASSWORD - value: "prometheus" + valueFrom: + secretKeyRef: + name: eduide-shared-cache-reposilite-secrets + key: prometheusPassword - # Mount shared configuration + plugins into Reposilite pod deployment: initContainers: - name: download-prometheus-plugin