diff --git a/charts/hawkbit/.helmignore b/charts/hawkbit/.helmignore index 50af0317..7aff2ccb 100644 --- a/charts/hawkbit/.helmignore +++ b/charts/hawkbit/.helmignore @@ -20,3 +20,5 @@ .idea/ *.tmproj .vscode/ +# Packaged chart tarballs +*.tgz diff --git a/charts/hawkbit/Chart.yaml b/charts/hawkbit/Chart.yaml index f2bfa03f..b7608b86 100644 --- a/charts/hawkbit/Chart.yaml +++ b/charts/hawkbit/Chart.yaml @@ -11,7 +11,7 @@ --- apiVersion: v2 version: 1.7.0 -appVersion: "0.5.0-mysql" +appVersion: "0.10.0" description: | Eclipse hawkBit™ is a domain independent back-end framework for rolling out software updates to constrained edge devices as well as more powerful controllers and gateways connected to @@ -32,11 +32,11 @@ maintainers: - name: ctron email: ctron@dentrassi.de dependencies: -- name: mysql - version: ^9.x - repository: "oci://registry-1.docker.io/bitnamicharts" - condition: mysql.enabled +- name: mariadb + version: ^0.x + repository: "oci://registry-1.docker.io/cloudpirates" + condition: mariadb.enabled - name: rabbitmq - version: ^10.x - repository: "oci://registry-1.docker.io/bitnamicharts" + version: ^0.x + repository: "oci://registry-1.docker.io/cloudpirates" condition: rabbitmq.enabled diff --git a/charts/hawkbit/templates/_helpers.tpl b/charts/hawkbit/templates/_helpers.tpl index a097f0e4..ad69e41c 100644 --- a/charts/hawkbit/templates/_helpers.tpl +++ b/charts/hawkbit/templates/_helpers.tpl @@ -54,3 +54,102 @@ Return the appropriate apiVersion for ingress. {{- print "networking.k8s.io/v1beta1" -}} {{- end -}} {{- end -}} + +{{/* +Return the secret with the Hawkbit credentials. +*/}} +{{- define "hawkbit.secretName" -}} + {{- if .Values.auth.existingSecret -}} + {{ print (tpl .Values.auth.existingSecret $) -}} + {{- else -}} + {{ printf "%s" (include "hawkbit.fullname" .) -}} + {{- end -}} +{{- end -}} + +{{- define "hawkbit.userCredentialsSecretName" -}} + {{- if .Values.auth.existingSecret -}} + {{- .Values.auth.existingSecret -}} + {{- else -}} + {{- printf "%s-user" (include "hawkbit.fullname" .) -}} + {{- end -}} +{{- end -}} + +{{- define "hawkbit.dbCredentialsSecretName" -}} + {{- if .Values.externalDatabase.existingSecret -}} + {{- .Values.externalDatabase.existingSecret -}} + {{- else -}} + {{- printf "%s-db" (include "hawkbit.fullname" .) -}} + {{- end -}} +{{- end -}} + +{{- define "hawkbit.rabbitmqCredentialsSecretName" -}} + {{- if .Values.rabbitmq.credentialsSecret -}} + {{- .Values.rabbitmq.credentialsSecret -}} + {{- else -}} + {{- printf "%s-rabbitmq-creds" (include "hawkbit.fullname" .) -}} + {{- end -}} +{{- end -}} + +{{/* +Database helpers — switch between externalDatabase and the bundled mariadb subchart. +*/}} + +{{- define "hawkbit.database.url" -}} + {{- if .Values.externalDatabase.url -}} + {{- .Values.externalDatabase.url -}} + {{- else if and .Values.externalDatabase.host (eq (.Values.externalDatabase.type | default "mariadb") "postgresql") -}} + {{- printf "jdbc:postgresql://%s:%v/%s" .Values.externalDatabase.host (.Values.externalDatabase.port | default 5432) (.Values.externalDatabase.database | default "hawkbit") -}} + {{- else if .Values.externalDatabase.host -}} + {{- printf "jdbc:mariadb://%s:%v/%s" .Values.externalDatabase.host (.Values.externalDatabase.port | default 3306) (.Values.externalDatabase.database | default "hawkbit") -}} + {{- else if .Values.mariadb.enabled -}} + {{- printf "jdbc:mariadb://%s-mariadb:3306/%s" (include "hawkbit.fullname" .) .Values.mariadb.auth.database -}} + {{- else -}} + {{- fail "Either externalDatabase.host or mariadb.enabled must be set" -}} + {{- end -}} +{{- end -}} + +{{- define "hawkbit.database.user" -}} + {{- if .Values.externalDatabase.user -}} + {{- .Values.externalDatabase.user -}} + {{- else if .Values.mariadb.enabled -}} + {{- "root" -}} + {{- else -}} + {{- fail "externalDatabase.user is required when mariadb.enabled=false" -}} + {{- end -}} +{{- end -}} + +{{- define "hawkbit.database.secretName" -}} + {{- if .Values.externalDatabase.existingSecret -}} + {{- .Values.externalDatabase.existingSecret -}} + {{- else if .Values.mariadb.enabled -}} + {{- include "mariadb.secretName" .Subcharts.mariadb -}} + {{- else -}} + {{- printf "%s-external-db" (include "hawkbit.fullname" .) -}} + {{- end -}} +{{- end -}} + +{{- define "hawkbit.database.secretPasswordKey" -}} + {{- if .Values.externalDatabase.existingSecretPasswordKey -}} + {{- .Values.externalDatabase.existingSecretPasswordKey -}} + {{- else if .Values.mariadb.enabled -}} + {{- "mariadb-root-password" -}} + {{- else -}} + {{- "password" -}} + {{- end -}} +{{- end -}} + +{{- define "hawkbit.database.secretUsernameKey" -}} + {{- if .Values.externalDatabase.existingSecretUsernameKey -}} + {{- .Values.externalDatabase.existingSecretUsernameKey -}} + {{- end -}} +{{- end -}} + +{{- define "hawkbit.spring.profiles" -}} + {{- if .Values.spring.profiles -}} + {{- .Values.spring.profiles -}} + {{- else if eq (.Values.externalDatabase.type | default "mariadb") "postgresql" -}} + {{- "postgresql" -}} + {{- else -}} + {{- "mysql" -}} + {{- end -}} +{{- end -}} diff --git a/charts/hawkbit/templates/configmap.yaml b/charts/hawkbit/templates/configmap.yaml index de11d2a2..e1f488f4 100644 --- a/charts/hawkbit/templates/configmap.yaml +++ b/charts/hawkbit/templates/configmap.yaml @@ -6,4 +6,44 @@ metadata: {{ include "hawkbit.labels" . | indent 4 }} data: application.yaml: |- -{{ toYaml .Values.config.application | indent 4}} + {{- $rabbitmqConfig := dict -}} + {{- if .Values.rabbitmq.enabled -}} + {{- $rabbitmqConfig = dict "spring" (dict + "rabbitmq" (dict + "host" (printf "%s-rabbitmq" .Release.Name) + "listener" (dict "simple" (dict "missing-queues-fatal" false)) + ) + "cloud" (dict "stream" (dict "bindings" (dict + "default" (dict "group" "hawkbit") + "device-created" (dict "destination" "device-registry.device-created") + "device-updated" (dict "destination" "device-registry.device-updated") + "device-deleted" (dict "destination" "device-registry.device-deleted") + ))) + ) -}} + {{- else if .Values.rabbitmq.host -}} + {{- $rabbitmqConfig = dict "spring" (dict + "rabbitmq" (dict + "host" .Values.rabbitmq.host + "listener" (dict "simple" (dict "missing-queues-fatal" false)) + ) + "cloud" (dict "stream" (dict "bindings" (dict + "default" (dict "group" "hawkbit") + "device-created" (dict "destination" "device-registry.device-created") + "device-updated" (dict "destination" "device-registry.device-updated") + "device-deleted" (dict "destination" "device-registry.device-deleted") + ))) + ) -}} + {{- else if not .Values.microservices.enabled -}} + {{- $rabbitmqConfig = dict "spring" (dict "autoconfigure" (dict "exclude" "org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration")) -}} + {{- end -}} + {{- $authConfig := dict "hawkbit" (dict "security" (dict "user" (dict (.Values.auth.username) (dict "tenant" "DEFAULT" "roles" (list "TENANT_ADMIN"))))) -}} + {{- $importConfig := dict "spring" (dict "config" (dict "import" (list "optional:file:/secret-config/application-user-credentials.yaml"))) -}} + {{- mergeOverwrite (deepCopy .Values.config.application) $rabbitmqConfig $authConfig $importConfig | toYaml | nindent 4 }} + application-mysql.yaml: |- + spring: + datasource: + url: {{ include "hawkbit.database.url" . | quote }} + application-postgresql.yaml: |- + spring: + datasource: + url: {{ include "hawkbit.database.url" . | quote }} diff --git a/charts/hawkbit/templates/deployment.yaml b/charts/hawkbit/templates/deployment.yaml index 614581af..1200387c 100644 --- a/charts/hawkbit/templates/deployment.yaml +++ b/charts/hawkbit/templates/deployment.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.microservices.enabled }} apiVersion: apps/v1 kind: Deployment metadata: @@ -25,6 +26,29 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} spec: + initContainers: + - name: hawkbit-init + image: "{{ .Values.image.initContainer }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + env: + - name: HAWKBIT_DB_MODE + value: "migrate" + - name: PROFILES + value: {{ include "hawkbit.spring.profiles" . | quote }} + - name: AND_THEN + value: "true" + envFrom: + - secretRef: + name: {{ include "hawkbit.dbCredentialsSecretName" . }} + volumeMounts: + - name: configmap + mountPath: {{ .Values.configMap.mountPath }} + - name: secret-config + mountPath: /secret-config + readOnly: true + {{- if .Values.extraVolumeMounts }} + {{- toYaml .Values.extraVolumeMounts | nindent 12 }} + {{- end }} {{- with .Values.image.pullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} @@ -34,36 +58,33 @@ spec: image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} env: - - name: SPRING_PROFILES_ACTIVE - value: "{{ .Values.spring.profiles }}" - - name: "SPRING_DATASOURCE_URL" - {{- if .Values.env.springDatasourceUrl }} - value: "{{ .Values.env.springDatasourceUrl }}" - {{- else }} - value: "jdbc:mariadb://{{ if .Values.mysql.enabled }}{{ .Release.Name }}-mysql{{ else }}{{ .Values.env.springDatasourceHost }}{{ end }}:3306/{{ .Values.env.springDatasourceDb }}" - {{- end }} - - name: "SPRING_APPLICATION_JSON" - valueFrom: - secretKeyRef: - name: {{ include "hawkbit.fullname" . }} - key: "SPRING_APPLICATION_JSON" - - name: "SPRING_RABBITMQ_HOST" - value: "{{ if .Values.rabbitmq.enabled }}{{ .Release.Name }}-rabbitmq{{ else }}{{ .Values.env.springRabbitmqHost }}{{ end }}" - - name: "SPRING_RABBITMQ_USERNAME" - value: "{{ .Values.env.springRabbitmqUsername }}" - - name: "SPRING_RABBITMQ_PASSWORD" - valueFrom: - secretKeyRef: - name: "{{ template "hawkbit.fullname" . }}-rabbitmq-pass" - key: "rabbitmq-pass" + - name: PROFILES + value: {{ include "hawkbit.spring.profiles" . | quote }} + - name: SPRING_FLYWAY_ENABLED + value: "false" {{- if .Values.fileStorage.enabled }} - - name: "org.eclipse.hawkbit.repository.file.path" + - name: ORG_ECLIPSE_HAWKBIT_ARTIFACT_FS_PATH value: {{ .Values.fileStorage.mountPath }} {{- end }} + {{- if .Values.extraEnv }} + {{- if kindIs "slice" .Values.extraEnv }} + {{- toYaml .Values.extraEnv | nindent 12 }} + {{- else if kindIs "map" .Values.extraEnv }} {{- range $key, $value := .Values.extraEnv }} - name: "{{ $key }}" value: "{{ $value }}" {{- end }} + {{- else }} + # .Values.extraEnv of type {{kindOf .Values.extraEnv}} is ignored + {{- end }} + {{- end }} + envFrom: + - secretRef: + name: {{ include "hawkbit.dbCredentialsSecretName" . }} + {{- if .Values.rabbitmq.enabled }} + - secretRef: + name: {{ include "hawkbit.rabbitmqCredentialsSecretName" . }} + {{- end }} ports: - name: http containerPort: 8080 @@ -83,12 +104,15 @@ spec: volumeMounts: - name: configmap mountPath: {{ .Values.configMap.mountPath }} + - name: secret-config + mountPath: /secret-config + readOnly: true {{- if .Values.fileStorage.enabled }} - name: storage mountPath: {{ .Values.fileStorage.mountPath }} {{- end }} {{- if .Values.extraVolumeMounts }} - {{ toYaml .Values.extraVolumeMounts | nindent 12 }} + {{- toYaml .Values.extraVolumeMounts | nindent 12 }} {{- end }} resources: {{ toYaml .Values.resources | indent 12 }} @@ -113,14 +137,23 @@ spec: {{- end }} {{- end }} volumes: - - name: configmap - configMap: - name: {{ include "hawkbit.fullname" . }} + - name: configmap + configMap: + name: {{ include "hawkbit.fullname" . }} + - name: secret-config + projected: + sources: + - secret: + name: {{ include "hawkbit.userCredentialsSecretName" . }} + items: + - key: application-user-credentials.yaml + path: application-user-credentials.yaml {{- if .Values.fileStorage.enabled }} - - name: storage - persistentVolumeClaim: - claimName: {{ include "hawkbit.fullname" . }}-data + - name: storage + persistentVolumeClaim: + claimName: {{ include "hawkbit.fullname" . }}-data {{- end}} {{- if .Values.extraVolumes }} - {{ toYaml .Values.extraVolumes | nindent 6 }} + {{- toYaml .Values.extraVolumes | nindent 8 }} {{- end }} +{{- end }} diff --git a/charts/hawkbit/templates/gui-deployment.yaml b/charts/hawkbit/templates/gui-deployment.yaml new file mode 100644 index 00000000..06b3b9ff --- /dev/null +++ b/charts/hawkbit/templates/gui-deployment.yaml @@ -0,0 +1,73 @@ +{{- if .Values.gui.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "hawkbit.fullname" . }}-gui + labels: +{{ include "hawkbit.labels" . | indent 4 }} + app.kubernetes.io/component: gui +spec: + replicas: {{ .Values.gui.replicaCount }} + selector: + matchLabels: + app.kubernetes.io/name: {{ include "hawkbit.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: gui + template: + metadata: + labels: + app.kubernetes.io/name: {{ include "hawkbit.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: gui + {{- with .Values.podTemplate.annotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.image.pullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: hawkbitgui + image: "{{ .Values.gui.image.repository }}:{{ .Values.gui.image.tag }}" + imagePullPolicy: {{ .Values.gui.image.pullPolicy }} + env: + - name: NEXTAUTH_URL + value: {{ .Values.gui.nextauth.url | quote }} + - name: NEXTAUTH_SECRET + valueFrom: + secretKeyRef: + name: {{ .Values.gui.nextauth.existingSecret }} + key: NEXTAUTH_SECRET + ports: + - name: http + containerPort: 3000 + protocol: TCP + livenessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 30 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 10 + timeoutSeconds: 5 + resources: +{{ toYaml .Values.gui.resources | indent 12 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/hawkbit/templates/gui-service.yaml b/charts/hawkbit/templates/gui-service.yaml new file mode 100644 index 00000000..fd0c9c2a --- /dev/null +++ b/charts/hawkbit/templates/gui-service.yaml @@ -0,0 +1,20 @@ +{{- if .Values.gui.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "hawkbit.fullname" . }}-gui + labels: +{{ include "hawkbit.labels" . | indent 4 }} + app.kubernetes.io/component: gui +spec: + type: ClusterIP + ports: + - port: {{ .Values.gui.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + app.kubernetes.io/name: {{ include "hawkbit.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: gui +{{- end }} diff --git a/charts/hawkbit/templates/ingress.yaml b/charts/hawkbit/templates/ingress.yaml index d024cc1c..a0af8f76 100644 --- a/charts/hawkbit/templates/ingress.yaml +++ b/charts/hawkbit/templates/ingress.yaml @@ -23,27 +23,69 @@ spec: {{- end }} {{- end }} {{- if eq $apiVersion "networking.k8s.io/v1" }} - ingressClassName: {{ .Values.ingress.ingressClassName }} + ingressClassName: {{ .Values.ingress.ingressClassName }} {{- end }} rules: {{- range .Values.ingress.hosts }} - host: {{ .host | quote }} http: paths: - {{- range .paths }} + {{- if $.Values.microservices.enabled }} + - path: /rest + pathType: Prefix + backend: + service: + name: {{ $fullName }}-mgmt + port: + name: http + {{- range $.Values.microservices.ddi.ingressPaths }} + - path: {{ . }} + pathType: Prefix + backend: + service: + name: {{ $fullName }}-ddi + port: + name: http + {{- end }} + {{- else }} + {{- if $.Values.gui.enabled }} + {{- /* monolith + gui: explicitly route api paths to monolith */}} + - path: /rest + pathType: Prefix + backend: + service: + name: {{ $fullName }} + port: + name: http + {{- range $.Values.microservices.ddi.ingressPaths }} + - path: {{ . }} + pathType: Prefix + backend: + service: + name: {{ $fullName }} + port: + name: http + {{- end }} + {{- else }} + {{- range .paths }} - path: {{ . }} - {{- if eq $apiVersion "networking.k8s.io/v1" }} pathType: Prefix backend: service: name: {{ $fullName }} port: name: http - {{- else }} + {{- end }} + {{- end }} + {{- end }} + {{- if $.Values.gui.enabled }} + - path: / + pathType: Prefix backend: - serviceName: {{ $fullName }} - servicePort: http - {{- end }} - {{- end }} + service: + name: {{ $fullName }}-gui + port: + name: http + {{- end }} {{- end }} {{- end }} diff --git a/charts/hawkbit/templates/microservices/ddi-deployment.yaml b/charts/hawkbit/templates/microservices/ddi-deployment.yaml new file mode 100644 index 00000000..3f4c58e9 --- /dev/null +++ b/charts/hawkbit/templates/microservices/ddi-deployment.yaml @@ -0,0 +1,139 @@ +{{- if .Values.microservices.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "hawkbit.fullname" . }}-ddi + labels: +{{ include "hawkbit.labels" . | indent 4 }} + app.kubernetes.io/component: ddi +spec: + replicas: {{ .Values.microservices.ddi.replicaCount }} + strategy: + type: Recreate + selector: + matchLabels: + app.kubernetes.io/name: {{ include "hawkbit.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: ddi + template: + metadata: + labels: + app.kubernetes.io/name: {{ include "hawkbit.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: ddi + annotations: + checksum/config: {{ include (print .Template.BasePath "/secrets.yaml") . | sha256sum }} + {{- with .Values.podTemplate.annotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + initContainers: + - name: hawkbit-init + image: "{{ .Values.image.initContainer }}:{{ .Values.microservices.ddi.image.tag }}" + imagePullPolicy: {{ .Values.microservices.ddi.image.pullPolicy }} + env: + - name: HAWKBIT_DB_MODE + value: "migrate" + - name: PROFILES + value: {{ include "hawkbit.spring.profiles" . | quote }} + - name: AND_THEN + value: "true" + envFrom: + - secretRef: + name: {{ include "hawkbit.dbCredentialsSecretName" . }} + volumeMounts: + - name: configmap + mountPath: {{ .Values.configMap.mountPath }} + - name: secret-config + mountPath: /secret-config + readOnly: true + containers: + - name: hawkbit-ddi + image: "{{ .Values.microservices.ddi.image.repository }}:{{ .Values.microservices.ddi.image.tag }}" + imagePullPolicy: {{ .Values.microservices.ddi.image.pullPolicy }} + env: + - name: PROFILES + value: {{ include "hawkbit.spring.profiles" . | quote }} + - name: SPRING_FLYWAY_ENABLED + value: "false" + {{- if .Values.fileStorage.enabled }} + - name: ORG_ECLIPSE_HAWKBIT_ARTIFACT_FS_PATH + value: {{ .Values.fileStorage.mountPath }} + {{- end }} + envFrom: + - secretRef: + name: {{ include "hawkbit.dbCredentialsSecretName" . }} + - secretRef: + name: {{ include "hawkbit.rabbitmqCredentialsSecretName" . }} + ports: + - name: http + containerPort: 8081 + protocol: TCP + livenessProbe: + httpGet: + path: /actuator/health + port: http + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + readinessProbe: + httpGet: + path: /actuator/health + port: http + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + volumeMounts: + - name: configmap + mountPath: {{ .Values.configMap.mountPath }} + {{- if .Values.fileStorage.enabled }} + - name: storage + mountPath: {{ .Values.fileStorage.mountPath }} + {{- end }} + - name: secret-config + mountPath: /secret-config + readOnly: true + {{- if .Values.extraVolumeMounts }} + {{- toYaml .Values.extraVolumeMounts | nindent 12 }} + {{- end }} + resources: +{{ toYaml .Values.microservices.ddi.resources | indent 12 }} + {{- 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.securityContext.enabled }} + securityContext: + fsGroup: {{ .Values.securityContext.fsGroup }} + runAsUser: {{ .Values.securityContext.runAsUser }} + {{- if .Values.securityContext.extra }} + {{- toYaml .Values.securityContext.extra | nindent 8 }} + {{- end }} + {{- end }} + volumes: + - name: configmap + configMap: + name: {{ include "hawkbit.fullname" . }} + {{- if .Values.fileStorage.enabled }} + - name: storage + persistentVolumeClaim: + claimName: {{ include "hawkbit.fullname" . }}-data + {{- end }} + - name: secret-config + projected: + sources: + - secret: + name: {{ include "hawkbit.userCredentialsSecretName" . }} + items: + - key: application-user-credentials.yaml + path: application-user-credentials.yaml + {{- if .Values.extraVolumes }} + {{- toYaml .Values.extraVolumes | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/hawkbit/templates/microservices/ddi-service.yaml b/charts/hawkbit/templates/microservices/ddi-service.yaml new file mode 100644 index 00000000..eb234938 --- /dev/null +++ b/charts/hawkbit/templates/microservices/ddi-service.yaml @@ -0,0 +1,20 @@ +{{- if .Values.microservices.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "hawkbit.fullname" . }}-ddi + labels: +{{ include "hawkbit.labels" . | indent 4 }} + app.kubernetes.io/component: ddi +spec: + type: ClusterIP + ports: + - port: 8081 + targetPort: http + protocol: TCP + name: http + selector: + app.kubernetes.io/name: {{ include "hawkbit.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: ddi +{{- end }} diff --git a/charts/hawkbit/templates/microservices/dmf-deployment.yaml b/charts/hawkbit/templates/microservices/dmf-deployment.yaml new file mode 100644 index 00000000..b53df7f3 --- /dev/null +++ b/charts/hawkbit/templates/microservices/dmf-deployment.yaml @@ -0,0 +1,110 @@ +{{- if .Values.microservices.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "hawkbit.fullname" . }}-dmf + labels: +{{ include "hawkbit.labels" . | indent 4 }} + app.kubernetes.io/component: dmf +spec: + replicas: {{ .Values.microservices.dmf.replicaCount }} + strategy: + type: Recreate + selector: + matchLabels: + app.kubernetes.io/name: {{ include "hawkbit.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: dmf + template: + metadata: + labels: + app.kubernetes.io/name: {{ include "hawkbit.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: dmf + annotations: + checksum/config: {{ include (print .Template.BasePath "/secrets.yaml") . | sha256sum }} + {{- with .Values.podTemplate.annotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + initContainers: + - name: hawkbit-init + image: "{{ .Values.image.initContainer }}:{{ .Values.microservices.dmf.image.tag }}" + imagePullPolicy: {{ .Values.microservices.dmf.image.pullPolicy }} + env: + - name: HAWKBIT_DB_MODE + value: "migrate" + - name: PROFILES + value: {{ include "hawkbit.spring.profiles" . | quote }} + - name: AND_THEN + value: "true" + envFrom: + - secretRef: + name: {{ include "hawkbit.dbCredentialsSecretName" . }} + volumeMounts: + - name: configmap + mountPath: {{ .Values.configMap.mountPath }} + - name: secret-config + mountPath: /secret-config + readOnly: true + containers: + - name: hawkbit-dmf + image: "{{ .Values.microservices.dmf.image.repository }}:{{ .Values.microservices.dmf.image.tag }}" + imagePullPolicy: {{ .Values.microservices.dmf.image.pullPolicy }} + env: + - name: PROFILES + value: {{ include "hawkbit.spring.profiles" . | quote }} + - name: SPRING_FLYWAY_ENABLED + value: "false" + envFrom: + - secretRef: + name: {{ include "hawkbit.dbCredentialsSecretName" . }} + - secretRef: + name: {{ include "hawkbit.rabbitmqCredentialsSecretName" . }} + volumeMounts: + - name: configmap + mountPath: {{ .Values.configMap.mountPath }} + - name: secret-config + mountPath: /secret-config + readOnly: true + {{- if .Values.extraVolumeMounts }} + {{- toYaml .Values.extraVolumeMounts | nindent 12 }} + {{- end }} + resources: +{{ toYaml .Values.microservices.dmf.resources | indent 12 }} + {{- 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.securityContext.enabled }} + securityContext: + fsGroup: {{ .Values.securityContext.fsGroup }} + runAsUser: {{ .Values.securityContext.runAsUser }} + {{- if .Values.securityContext.extra }} + {{- toYaml .Values.securityContext.extra | nindent 8 }} + {{- end }} + {{- end }} + volumes: + - name: configmap + configMap: + name: {{ include "hawkbit.fullname" . }} + - name: secret-config + projected: + sources: + - secret: + name: {{ include "hawkbit.userCredentialsSecretName" . }} + items: + - key: application-user-credentials.yaml + path: application-user-credentials.yaml + {{- if .Values.extraVolumes }} + {{- toYaml .Values.extraVolumes | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/hawkbit/templates/microservices/mgmt-deployment.yaml b/charts/hawkbit/templates/microservices/mgmt-deployment.yaml new file mode 100644 index 00000000..501d39d2 --- /dev/null +++ b/charts/hawkbit/templates/microservices/mgmt-deployment.yaml @@ -0,0 +1,156 @@ +{{- if .Values.microservices.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "hawkbit.fullname" . }}-mgmt + labels: +{{ include "hawkbit.labels" . | indent 4 }} + app.kubernetes.io/component: mgmt +spec: + replicas: {{ .Values.microservices.mgmt.replicaCount }} + strategy: + type: Recreate + selector: + matchLabels: + app.kubernetes.io/name: {{ include "hawkbit.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: mgmt + template: + metadata: + labels: + app.kubernetes.io/name: {{ include "hawkbit.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: mgmt + annotations: + checksum/config: {{ include (print .Template.BasePath "/secrets.yaml") . | sha256sum }} + {{- with .Values.podTemplate.annotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + initContainers: + - name: hawkbit-init + image: "{{ .Values.image.initContainer }}:{{ .Values.microservices.mgmt.image.tag }}" + imagePullPolicy: {{ .Values.microservices.mgmt.image.pullPolicy }} + env: + - name: HAWKBIT_DB_MODE + value: "migrate" + - name: PROFILES + value: {{ include "hawkbit.spring.profiles" . | quote }} + - name: AND_THEN + value: "true" + envFrom: + - secretRef: + name: {{ include "hawkbit.dbCredentialsSecretName" . }} + volumeMounts: + - name: configmap + mountPath: {{ .Values.configMap.mountPath }} + - name: secret-config + mountPath: /secret-config + readOnly: true + {{- if .Values.extraVolumeMounts }} + {{- toYaml .Values.extraVolumeMounts | nindent 12 }} + {{- end }} + {{- with .Values.microservices.mgmt.image.pullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: hawkbit-mgmt + image: "{{ .Values.microservices.mgmt.image.repository }}:{{ .Values.microservices.mgmt.image.tag }}" + imagePullPolicy: {{ .Values.microservices.mgmt.image.pullPolicy }} + env: + - name: PROFILES + value: {{ include "hawkbit.spring.profiles" . | quote }} + - name: SPRING_FLYWAY_ENABLED + value: "false" + {{- if .Values.fileStorage.enabled }} + - name: ORG_ECLIPSE_HAWKBIT_ARTIFACT_FS_PATH + value: {{ .Values.fileStorage.mountPath }} + {{- end }} + {{- if .Values.extraEnv }} + {{- if kindIs "slice" .Values.extraEnv }} + {{- toYaml .Values.extraEnv | nindent 12 }} + {{- else if kindIs "map" .Values.extraEnv }} + {{- range $key, $value := .Values.extraEnv }} + - name: "{{ $key }}" + value: "{{ $value }}" + {{- end }} + {{- end }} + {{- end }} + envFrom: + - secretRef: + name: {{ include "hawkbit.dbCredentialsSecretName" . }} + - secretRef: + name: {{ include "hawkbit.rabbitmqCredentialsSecretName" . }} + ports: + - name: http + containerPort: 8080 + protocol: TCP + livenessProbe: + httpGet: + path: /actuator/health + port: http + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + readinessProbe: + httpGet: + path: /actuator/health + port: http + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + volumeMounts: + - name: configmap + mountPath: {{ .Values.configMap.mountPath }} + - name: secret-config + mountPath: /secret-config + readOnly: true + {{- if .Values.fileStorage.enabled }} + - name: storage + mountPath: {{ .Values.fileStorage.mountPath }} + {{- end }} + {{- if .Values.extraVolumeMounts }} + {{- toYaml .Values.extraVolumeMounts | nindent 12 }} + {{- end }} + resources: +{{ toYaml .Values.microservices.mgmt.resources | indent 12 }} + {{- 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.securityContext.enabled }} + securityContext: + fsGroup: {{ .Values.securityContext.fsGroup }} + runAsUser: {{ .Values.securityContext.runAsUser }} + {{- if .Values.securityContext.extra }} + {{- toYaml .Values.securityContext.extra | nindent 8 }} + {{- end }} + {{- end }} + volumes: + - name: configmap + configMap: + name: {{ include "hawkbit.fullname" . }} + - name: secret-config + projected: + sources: + - secret: + name: {{ include "hawkbit.userCredentialsSecretName" . }} + items: + - key: application-user-credentials.yaml + path: application-user-credentials.yaml + {{- if .Values.fileStorage.enabled }} + - name: storage + persistentVolumeClaim: + claimName: {{ include "hawkbit.fullname" . }}-data + {{- end }} + {{- if .Values.extraVolumes }} + {{- toYaml .Values.extraVolumes | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/hawkbit/templates/microservices/mgmt-service.yaml b/charts/hawkbit/templates/microservices/mgmt-service.yaml new file mode 100644 index 00000000..a801b569 --- /dev/null +++ b/charts/hawkbit/templates/microservices/mgmt-service.yaml @@ -0,0 +1,20 @@ +{{- if .Values.microservices.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "hawkbit.fullname" . }}-mgmt + labels: +{{ include "hawkbit.labels" . | indent 4 }} + app.kubernetes.io/component: mgmt +spec: + type: ClusterIP + ports: + - port: 8080 + targetPort: http + protocol: TCP + name: http + selector: + app.kubernetes.io/name: {{ include "hawkbit.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: mgmt +{{- end }} diff --git a/charts/hawkbit/templates/pvc.yaml b/charts/hawkbit/templates/pvc.yaml index 4be6f9ef..0ce880b9 100644 --- a/charts/hawkbit/templates/pvc.yaml +++ b/charts/hawkbit/templates/pvc.yaml @@ -11,9 +11,9 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: {{ .Values.fileStorage.pvcSize }} + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.fileStorage.pvcSize }} {{- end }} diff --git a/charts/hawkbit/templates/secrets.yaml b/charts/hawkbit/templates/secrets.yaml index 2def1c4a..ae58671e 100644 --- a/charts/hawkbit/templates/secrets.yaml +++ b/charts/hawkbit/templates/secrets.yaml @@ -1,22 +1,55 @@ +{{- if not .Values.auth.existingSecret }} apiVersion: v1 kind: Secret metadata: - name: {{ template "hawkbit.fullname" . }} + name: {{ template "hawkbit.fullname" . }}-user labels: {{ include "hawkbit.labels" . | indent 4 }} type: Opaque -data: - SPRING_APPLICATION_JSON: {{ .Values.config.secrets | toJson | b64enc }} +stringData: + application-user-credentials.yaml: |- + hawkbit: + security: + user: + {{ .Values.auth.username }}: + password: {{ .Values.auth.password | quote }} --- +{{- end }} +{{- if and (not .Values.externalDatabase.existingSecret) (not .Values.mariadb.enabled) .Values.externalDatabase.host }} apiVersion: v1 kind: Secret metadata: - name: {{ template "hawkbit.fullname" . }}-rabbitmq-pass + name: {{ template "hawkbit.fullname" . }}-db labels: - app.kubernetes.io/name: {{ include "hawkbit.name" . }} - helm.sh/chart: {{ include "hawkbit.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} +{{ include "hawkbit.labels" . | indent 4 }} +type: Opaque +stringData: + SPRING_DATASOURCE_USERNAME: {{ include "hawkbit.database.user" . | quote }} + SPRING_DATASOURCE_PASSWORD: {{ .Values.externalDatabase.password | quote }} +--- +{{- end }} +{{- if and (not .Values.externalDatabase.existingSecret) .Values.mariadb.enabled }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "hawkbit.fullname" . }}-db + labels: +{{ include "hawkbit.labels" . | indent 4 }} +type: Opaque +stringData: + SPRING_DATASOURCE_USERNAME: "root" + SPRING_DATASOURCE_PASSWORD: {{ .Values.mariadb.auth.rootPassword | default "" | quote }} +--- +{{- end }} +{{- if and .Values.rabbitmq.enabled (not .Values.rabbitmq.credentialsSecret) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "hawkbit.fullname" . }}-rabbitmq-creds + labels: +{{ include "hawkbit.labels" . | indent 4 }} type: Opaque -data: - rabbitmq-pass: {{ .Values.env.springRabbitmqPassword | b64enc | quote }} +stringData: + SPRING_RABBITMQ_USERNAME: {{ .Values.rabbitmq.auth.username | quote }} + SPRING_RABBITMQ_PASSWORD: {{ .Values.rabbitmq.auth.password | quote }} +{{- end }} diff --git a/charts/hawkbit/templates/service.yaml b/charts/hawkbit/templates/service.yaml index db234663..919987ca 100644 --- a/charts/hawkbit/templates/service.yaml +++ b/charts/hawkbit/templates/service.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.microservices.enabled }} apiVersion: v1 kind: Service metadata: @@ -18,3 +19,4 @@ spec: selector: app.kubernetes.io/name: {{ include "hawkbit.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} diff --git a/charts/hawkbit/templates/tests/test-connection.yaml b/charts/hawkbit/templates/tests/test-connection.yaml index 1cc847ab..aa38fa89 100644 --- a/charts/hawkbit/templates/tests/test-connection.yaml +++ b/charts/hawkbit/templates/tests/test-connection.yaml @@ -16,7 +16,7 @@ spec: command: ['curl'] args: [ "-X", "GET", - "-u", "{{ .Values.config.application.spring.security.user.name }}:{{ trimPrefix "{noop}" .Values.config.secrets.spring.security.user.password }}", + "-u", "{{ .Values.auth.username }}:{{ trimPrefix "{noop}" .Values.auth.password }}", "http://{{ include "hawkbit.fullname" . }}:{{ .Values.service.port }}/rest/v1/userinfo" ] restartPolicy: Never diff --git a/charts/hawkbit/values.yaml b/charts/hawkbit/values.yaml index 44a4c17b..7e05ca60 100644 --- a/charts/hawkbit/values.yaml +++ b/charts/hawkbit/values.yaml @@ -15,8 +15,17 @@ image: repository: "hawkbit/hawkbit-update-server" - tag: 0.5.0-mysql + tag: '0.10.0' pullPolicy: IfNotPresent + initContainer: "hawkbit/hawkbit-repository-jpa-init" + +# auth configuration +auth: + # if set, must contain key: application-user-credentials.yaml + existingSecret: "" + username: "admin" + password: "{noop}admin" + dmfPassword: "[KEYCLOAK_HAWKBIT_USER_PASSWORD]" replicaCount: 1 @@ -78,21 +87,12 @@ routes: fileStorage: enabled: true pvcSize: "1Gi" - mountPath: "/var/lib/hawkbit-storage" - -# env vars for configuration -env: - springDatasourceHost: "hawkbit-mysql" - springDatasourceDb: "hawkbit" - # if springDatasourceUrl is set override default mysql db url - springDatasourceUrl: "" - springRabbitmqHost: "hawkbit-rabbitmq" - springRabbitmqUsername: "hawkbit" - springRabbitmqPassword: "hawkbit" + mountPath: "/artifactrepo" # optional env vars -extraEnv: {} - # JAVA_TOOL_OPTIONS: "-Xms1024m -Xmx1024m" +extraEnv: [] +# - name: JAVA_TOOL_OPTIONS: +# value: "-Xms1024m -Xmx1024m" resources: {} # We usually recommend not to specify default resources and to leave this as a conscious @@ -119,7 +119,7 @@ extraVolumes: [] extraVolumeMounts: [] configMap: - mountPath: "/opt/hawkbit/config" + mountPath: "/app/config" spring: profiles: "mysql" @@ -129,75 +129,97 @@ config: server: useForwardHeaders: true hawkbit: - ## Configuration for the device management federation - ## ref: https://www.eclipse.org/hawkbit/apis/dmf_api/ - ## These configuration will become available once https://github.com/eclipse/hawkbit/pull/890 is merged - # dmf: - # hono: - # enabled: false - # tenant-list-uri: "http://[DEVICE_REGISTRY_HOST]:8080/admin/tenants" - # device-list-uri: "http://[DEVICE_REGISTRY_HOST]:8080/admin/$$tenantId/devices" - # credentials-list-uri: "http://[DEVICE_REGISTRY_HOST]:8080/v1/credentials/$$tenantId/$$deviceId" - # authentication-method: "oidc" - # username: "[KEYCLOAK_HAWKBIT_USERNAME]" - # oidc-token-uri: "http://[KEYCLOAK_HOST]:8080/auth/realms/master/protocol/openid-connect/token" - # oidc-client-id: "[KEYCLOAK_DEVICE_REGISTRY_CLIENT_ID]" - spring: - cloud: - stream: - bindings: - default: - group: "hawkbit" - device-created: - destination: "device-registry.device-created" - device-updated: - destination: "device-registry.device-updated" - device-deleted: - destination: "device-registry.device-deleted" - security: - user: - name: admin - secrets: - hawkbit: - dmf: - hono: - password: "[KEYCLOAK_HAWKBIT_USER_PASSWORD]" - spring: - security: - user: - # the "{noop}" prefix is needed! - password: "{noop}admin" - datasource: - username: hawkbit - password: hawkbit - ## dependency charts config -## ref: https://github.com/bitnami/charts/blob/master/bitnami/mysql/values.yaml -mysql: +## External database configuration (used when mariadb.enabled=false). +## type: mariadb (default) or postgresql +externalDatabase: + type: mariadb + host: "" + port: "" + database: hawkbit + user: "" + password: "" # used when existingSecret is not set + url: "" # optional: override the full JDBC URL + existingSecret: "" # must contain keys: SPRING_DATASOURCE_USERNAME, SPRING_DATASOURCE_PASSWORD + +## ref: https://github.com/CloudPirates-io/helm-charts/tree/main/charts/mariadb +mariadb: enabled: true - primary: - persistence: - enabled: true - volumePermissions: + persistence: enabled: true - architecture: standalone auth: + existingSecret: "" + rootPassword: "" + database: hawkbit username: hawkbit password: hawkbit - database: hawkbit + secretKeys: + rootPasswordKey: mariadb-root-password + userPasswordKey: mariadb-password metrics: enabled: true -## ref: https://github.com/bitnami/charts/blob/master/bitnami/rabbitmq/values.yaml +## ref: https://github.com/CloudPirates-io/helm-charts/tree/main/charts/rabbitmq rabbitmq: + # hawkbit chart: if set, must contain keys SPRING_RABBITMQ_USERNAME and SPRING_RABBITMQ_PASSWORD; + # generated from rabbitmq.auth.username and rabbitmq.auth.password if not set + credentialsSecret: "" + # external RabbitMQ hostname (used when rabbitmq.enabled=false and microservices.enabled=true) + host: "" enabled: true persistence: enabled: true - volumePermissions: - enabled: true auth: + existingSecret: "" + existingPasswordKey: "rabbitmq-password" + existingErlangCookieKey: "rabbitmq-erlang-cookie" username: hawkbit password: hawkbit + erlangCookie: "" metrics: enabled: true + +## Microservices mode — deploy mgmt, ddi, dmf as separate pods instead of the monolith +microservices: + enabled: false + mgmt: + image: + repository: "hawkbit/hawkbit-mgmt-server" + tag: "0.10.0" + pullPolicy: IfNotPresent + replicaCount: 1 + resources: {} + ddi: + image: + repository: "hawkbit/hawkbit-ddi-server" + tag: "0.10.0" + pullPolicy: IfNotPresent + replicaCount: 1 + resources: {} + # Ingress paths routed to the DDI server (e.g., ["/DEFAULT/controller"]) + ingressPaths: [] + dmf: + image: + repository: "hawkbit/hawkbit-dmf-server" + tag: "0.10.0" + pullPolicy: IfNotPresent + replicaCount: 1 + resources: {} + +## hawkbitgui — optional Next.js management UI +gui: + enabled: false + image: + repository: "ghcr.io/joshua-clayton/hawkbitgui" + tag: "0.1.0" + pullPolicy: IfNotPresent + replicaCount: 1 + service: + port: 3000 + nextauth: + # NEXTAUTH_URL: full public URL of the GUI (e.g., https://hawkbit.example.com) + url: "http://localhost:3000" + # existingSecret must contain key: NEXTAUTH_SECRET + existingSecret: "" + resources: {}