diff --git a/charts/altinn-app/.helmignore b/charts/altinn-app/.helmignore new file mode 100644 index 0000000..f0c1319 --- /dev/null +++ b/charts/altinn-app/.helmignore @@ -0,0 +1,21 @@ +# 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 +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/charts/altinn-app/Chart.yaml b/charts/altinn-app/Chart.yaml new file mode 100644 index 0000000..69793f7 --- /dev/null +++ b/charts/altinn-app/Chart.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +name: altinn-app +description: A Helm chart for Altinn apps +version: 0.0.1 +appVersion: "latest" + +sources: + - https://github.com/Altinn/altinn-studio-charts + \ No newline at end of file diff --git a/charts/altinn-app/templates/NOTES.txt b/charts/altinn-app/templates/NOTES.txt new file mode 100644 index 0000000..fdea615 --- /dev/null +++ b/charts/altinn-app/templates/NOTES.txt @@ -0,0 +1,6 @@ +{{ .Values.appName }} is installed +The release is named {{ .Release.Name }} + + To learn more about the release, try: + $ helm status {{ .Release.name }} + $ helm get {{ .Release.name }} \ No newline at end of file diff --git a/charts/altinn-app/templates/_helpers.tpl b/charts/altinn-app/templates/_helpers.tpl new file mode 100644 index 0000000..2b63583 --- /dev/null +++ b/charts/altinn-app/templates/_helpers.tpl @@ -0,0 +1,58 @@ +{{/* +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 ".Chart.Name" -}} +{{- 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 ".Chart.Name.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define ".Chart.Name.labels" -}} +helm.sh/chart: {{ include ".Chart.Name.chart" . }} +{{ include ".Chart.Name.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define ".Chart.Name.selectorLabels" -}} +app.kubernetes.io/name: {{ .Values.appName }} +app.kubernetes.io/instance: {{ .Release.Name }} +app: {{ .Values.appName }} +release: {{ .Release.Name }} +team: {{ .Values.team }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define ".Chart.Name.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include ".Chart.Name" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/charts/altinn-app/templates/deployment.yaml b/charts/altinn-app/templates/deployment.yaml new file mode 100644 index 0000000..944a439 --- /dev/null +++ b/charts/altinn-app/templates/deployment.yaml @@ -0,0 +1,104 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Values.appName }} + labels: + {{- include ".Chart.Name.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include ".Chart.Name.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- if .Values.linkerd.enabled }} + annotations: + linkerd.io/inject: enabled + config.linkerd.io/skip-outbound-ports: 9092,10255,9093,443 + azure.workload.identity/skip-containers: linkerd-proxy;linkerd-init + {{- end }} + labels: + {{- include ".Chart.Name.selectorLabels" . | nindent 8 }} + azure.workload.identity/use: "true" + spec: + serviceAccountName: {{ .Values.appName }} + containers: + - name: {{ .Values.appName }} + image: "{{ .Values.image.repository }}:{{ required "image.tag not set" .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + allowPrivilegeEscalation: false + ports: + - containerPort: {{ .Values.image.containerPort }} + env: + {{- if not (hasKey .Values.environmentVariables .Values.environment) }} + {{ fail "the chosen environment does not exist" }} + {{- end }} + {{- $environmentVariables := index $.Values.environmentVariables $.Values.environment }} + {{- range $variable := $environmentVariables }} + - name: {{ $variable.name }} + {{- if $variable.value }} + value: {{ $variable.value | quote }} + {{- end }} + {{- if $variable.valueFrom }} + valueFrom: + {{- if $variable.valueFrom.secretKeyRef}} + secretKeyRef: + name: {{ $variable.valueFrom.secretKeyRef.name }} + key: {{ $variable.valueFrom.secretKeyRef.key }} + {{- end }} + {{- end }} + {{- end }} + readinessProbe: + httpGet: + path: /health + port: {{ .Values.service.internalPort }} + + initialDelaySeconds: 30 + failureThreshold: 3 + periodSeconds: 3 + timeoutSeconds: 1 + livenessProbe: + httpGet: + path: /health + port: {{ .Values.service.internalPort }} + initialDelaySeconds: 3 + failureThreshold: 3 + periodSeconds: 10 + timeoutSeconds: 2 + {{- $resources := index $.Values.resources $.Values.environment }} + resources: + {{- toYaml $resources| nindent 12 }} + {{- if .Values.volumeMounts}} + volumeMounts: + {{- range $mount := .Values.volumeMounts}} + - name: {{ $mount.name }} + mountPath: {{ $mount.mountPath }} + {{- end }} + {{- end }} + {{- if .Values.volumes }} + volumes: + {{- range $volume := .Values.volumes }} + - name: {{ $volume.name }} + {{- if $volume.persistantVolumeClaim }} + persistantVolumeClaim: + claimName: {{ $volume.persistantVolumeClaim.claimName }} + {{- end }} + {{- if $volume.secret }} + secret: + secretName: {{ $volume.secret.secretName }} + {{- end }} + {{- end }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.image.imagePullSecret }} + imagePullSecrets: + - name: {{ .Values.image.imagePullSecret }} + {{- end }} diff --git a/charts/altinn-app/templates/hpa.yaml b/charts/altinn-app/templates/hpa.yaml new file mode 100644 index 0000000..348b46d --- /dev/null +++ b/charts/altinn-app/templates/hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ .Values.appName }} + labels: + {{- include ".Chart.Name.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ .Values.appName }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageValue: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageValue: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/charts/altinn-app/templates/ingressRoute.yaml b/charts/altinn-app/templates/ingressRoute.yaml new file mode 100644 index 0000000..b897969 --- /dev/null +++ b/charts/altinn-app/templates/ingressRoute.yaml @@ -0,0 +1,42 @@ +{{- $environment := .Values.environment }} +{{- $linkerd := .Values.linkerd }} + +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: {{ .Values.ingressRoute.name }} + labels: + {{- include ".Chart.Name.labels" . | nindent 4 }} +spec: + entryPoints: + {{- range $entryPoint := .Values.ingressRoute.entryPoints }} + - {{ $entryPoint }} + {{- end }} + routes: + {{- range $route := .Values.ingressRoute.routes }} + {{- $routeMatch := index $route.match $environment }} + - match: {{ $routeMatch }} + kind: {{ $route.kind }} + {{- if $route.priority }} + priority: {{ $route.priority }} + {{- end }} + services: + {{- range $service := $route.services }} + - name: {{ $service.name }} + port: {{ $service.port }} + {{- end }} + {{- if or $route.middlewares $linkerd.enabled }} + middlewares: + {{- if $route.middlewares }} + {{- range $middleware := $route.middlewares}} + - name: {{ $middleware.name }} + {{- end }} + {{- end }} + {{- if $linkerd.enabled }} + - name: {{ $linkerd.header }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.ingressRoute.tls }} + {{- end }} + diff --git a/charts/altinn-app/templates/jobs.yaml b/charts/altinn-app/templates/jobs.yaml new file mode 100644 index 0000000..0ab0f21 --- /dev/null +++ b/charts/altinn-app/templates/jobs.yaml @@ -0,0 +1,54 @@ +{{- if .Values.jobs }} +{{- range $jobName, $job := .Values.jobs }} +--- +apiVersion: batch/v1 +kind: {{ $job.kind | default "Job" }} +metadata: + name: {{ $jobName }} + labels: + {{- include ".Chart.Name.labels" . | nindent 4 }} +spec: + {{- if eq $job.kind "CronJob" }} + schedule: "{{ $job.schedule }}" + concurrencyPolicy: {{ $job.concurrencyPolicy | default "Forbid" }} + successfulJobsHistoryLimit: {{ $job.successfulJobsHistoryLimit | default 1 }} + failedJobsHistoryLimit: {{ $job.failedJobsHistoryLimit | default 1 }} + jobTemplate: + spec: + template: + spec: + containers: + - name: {{ $jobName }} + image: {{ $job.image }} + command: + {{- tpl (toYaml $job.command) $ | nindent 12 }} + {{- if $job.env }} + env: + {{- range $key, $value := $job.env }} + - name: {{ $key }} + value: "{{ $value }}" + {{- end }} + {{- end }} + restartPolicy: {{ $job.restartPolicy | default "Never" }} + {{- with $job.backoffLimit }} + backoffLimit: {{ . }} + {{- end }} + {{- else }} + template: + spec: + containers: + - name: {{ $jobName }} + image: {{ $job.image }} + command: + {{- tpl (toYaml $job.command) $ | nindent 10 }} + {{- if $job.env }} + env: + {{- range $key, $value := $job.env }} + - name: {{ $key }} + value: "{{ $value }}" + {{- end }} + {{- end }} + restartPolicy: {{ $job.restartPolicy | default "Never" }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/altinn-app/templates/middleware.yaml b/charts/altinn-app/templates/middleware.yaml new file mode 100644 index 0000000..a5e6cad --- /dev/null +++ b/charts/altinn-app/templates/middleware.yaml @@ -0,0 +1,18 @@ +{{- if .Values.linkerd.enabled }} +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: {{ .Values.linkerd.header }} + labels: + {{- include ".Chart.Name.labels" . | nindent 4 }} +spec: + headers: + customRequestHeaders: + l5d-dst-override: "{{ .Values.linkerd.host }}" +--- +{{- end }} + +{{- range $middleware := .Values.middlewares }} +{{ toYaml $middleware }} +--- +{{- end }} diff --git a/charts/altinn-app/templates/service.yaml b/charts/altinn-app/templates/service.yaml new file mode 100644 index 0000000..8ab94f6 --- /dev/null +++ b/charts/altinn-app/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.appName }} + labels: + {{- include ".Chart.Name.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.externalPort }} + targetPort: {{ .Values.service.internalPort }} + protocol: TCP + name: {{ .Values.service.name }} + selector: + app: {{ .Values.appName }} + release: {{ .Release.Name }} diff --git a/charts/altinn-app/templates/serviceaccount.yaml b/charts/altinn-app/templates/serviceaccount.yaml new file mode 100644 index 0000000..0dd5385 --- /dev/null +++ b/charts/altinn-app/templates/serviceaccount.yaml @@ -0,0 +1,10 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Values.appName }} + labels: + {{- include ".Chart.Name.labels" . | nindent 4 }} + annotations: + azure.workload.identity/client-id: "{{ .Values.clientId }}" +{{- end }} diff --git a/charts/altinn-app/templates/slos.yaml b/charts/altinn-app/templates/slos.yaml new file mode 100644 index 0000000..134a359 --- /dev/null +++ b/charts/altinn-app/templates/slos.yaml @@ -0,0 +1,22 @@ +{{- if or (eq .Values.environment "prod") (eq .Values.environment "tt02") }} +apiVersion: pyrra.dev/v1alpha1 +kind: ServiceLevelObjective +metadata: + name: {{ .Values.ingressRoute.name }} + namespace: monitoring + labels: + {{- include ".Chart.Name.labels" . | nindent 4 }} + pyrra.dev/team: {{ .Values.team }} + pyrra.dev/app: {{ .Values.appName }} + release: kube-prometheus-stack + role: alert-rules +spec: + indicator: + ratio: + errors: + metric: traefik_service_requests_total{service=~".*{{ .Values.ingressRoute.name }}.*",code=~"5.."} + total: + metric: traefik_service_requests_total{service=~".*{{ .Values.ingressRoute.name }}.*"} + target: {{ .Values.slo.target }} + window: 4w +{{- end }} diff --git a/charts/altinn-app/values.yaml b/charts/altinn-app/values.yaml new file mode 100644 index 0000000..92f94dd --- /dev/null +++ b/charts/altinn-app/values.yaml @@ -0,0 +1,100 @@ +environment: dev + +aksClusterName: "dev" + +appName: "altinn-example" +team: "altinn" + +slo: + target: "99" +# workload identity +envs: + dev: + azureClientId: "000000-0000-0000-0000-000000000000" +# someClusterName: +# azureClientId: "000000-0000-0000-0000-000000000000" + +image: + # repository: altinntjenesterregistry.azurecr.io/altinn-example + pullPolicy: IfNotPresent + containerPort: 5090 + +environmentVariables: + dev: + # - name: myenv + # value: myvalue + + # prod + + +volumeMounts: + - name: altinn-appsettings + mountPath: "/altinn-appsettings" + +volumes: + - name: altinn-appsettings + secret: + secretName: altinn-appsettings-secret + +resources: + dev: + requests: + cpu: 500m + memory: 128Mi + # prod: + # requests: + # cpu: 500m + # memory: 128Mi + +autoscaling: + enabled: false + # minReplicas: 2 + # maxReplicas: 8 + # targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +## set replica count if autoscaling is disabled +replicaCount: 2 + +service: + type: ClusterIP + externalPort: 80 + internalPort: 5090 + +serviceAccount: + create: true + annotations: + azure.workload.identity/client-id: "" +linkerd: + enabled: true + header: altinn-example + host: altinn-example.default.svc.cluster.local:80 + +ingressRoute: + name: altinn-example-ingress-route + entryPoints: + - http + - https + routes: + - match: + dev: Host(`internal.platform.dev.altinn.cloud`) && PathPrefix(`/example`) + # prod: Host(`internal.platform.prod.altinn.cloud`) && PathPrefix(`/example`) + kind: Rule + middlewares: + - name: hsts-header + services: + - name: altinn-example + port: 80 + + +# jobs: +# my-cron-job: +# kind: CronJob +# schedule: "*/1 * * * *" +# image: curlimages/curl:latest +# command: +# - "curl" +# - "--fail" +# - "-X" +# - "DELETE" +# - "http://altinn-example.default.svc.cluster.local/example/api/v1/trigger/myendpoint"