diff --git a/.ci/mkdocs/requirements.txt b/.ci/mkdocs/requirements.txt index 374ebb72..55585f08 100644 --- a/.ci/mkdocs/requirements.txt +++ b/.ci/mkdocs/requirements.txt @@ -1,5 +1,5 @@ mkdocs==1.3.1 mkdocs-macros-plugin ==0.7.0 -mkdocs-material ==8.4.4 +mkdocs-material ==8.5.0 mkdocs-minify-plugin==0.5.0 mkdocs-redirects==1.1.0 diff --git a/charts/library/common/Chart.yaml b/charts/library/common/Chart.yaml index e477da1d..69edd259 100644 --- a/charts/library/common/Chart.yaml +++ b/charts/library/common/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: common description: Function library for Helm charts type: library -version: 0.1.0 +version: 0.2.0 kubeVersion: ">=1.16.0-0" keywords: - common @@ -13,5 +13,15 @@ maintainers: email: me@bjw-s.dev annotations: artifacthub.io/changes: |- + - kind: removed + description: BREAKING - Removed support for the `openvpn` and `wireguard` VPN types. + - kind: added + description: Added support for adding serviceMonitors to services. - kind: changed - description: Initial version + description: ConfigMap checksum logic now only looks at ConfigMap data + - kind: changed + description: Explicitly convert defaultMode to decimal notation in code-server addon. + - kind: changed + description: Updated gluetun image to v3.31.1 + - kind: changed + description: Updated code-server image to v4.7.0 diff --git a/charts/library/common/README.md b/charts/library/common/README.md index a5bcb9d5..8721e3f4 100644 --- a/charts/library/common/README.md +++ b/charts/library/common/README.md @@ -1,6 +1,6 @@ # common -![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: library](https://img.shields.io/badge/Type-library-informational?style=flat-square) +![Version: 0.2.0](https://img.shields.io/badge/Version-0.2.0-informational?style=flat-square) ![Type: library](https://img.shields.io/badge/Type-library-informational?style=flat-square) Function library for Helm charts @@ -29,7 +29,7 @@ Include this chart as a dependency in your `Chart.yaml` e.g. # Chart.yaml dependencies: - name: common - version: 0.1.0 + version: 0.2.0 repository: https://bjw-s.github.io/helm-charts/ ``` @@ -61,7 +61,7 @@ N/A | addons.codeserver.git.deployKeySecret | string | `""` | Existing secret containing SSH private key The chart expects it to be present under the `id_rsa` key. | | addons.codeserver.image.pullPolicy | string | `"IfNotPresent"` | Specify the code-server image pull policy | | addons.codeserver.image.repository | string | `"ghcr.io/coder/code-server"` | Specify the code-server image | -| addons.codeserver.image.tag | string | `"4.5.1"` | Specify the code-server image tag | +| addons.codeserver.image.tag | string | `"4.7.0"` | Specify the code-server image tag | | addons.codeserver.ingress.enabled | bool | `false` | Enable an ingress for the code-server add-on. | | addons.codeserver.ingress.ingressClassName | string | `nil` | Set the ingressClass that is used for this ingress. Requires Kubernetes >=1.19 | | addons.codeserver.service.enabled | bool | `true` | Enable a service for the code-server add-on. | @@ -83,7 +83,7 @@ N/A | addons.promtail.logs | list | `[]` | The paths to logs on the volume | | addons.promtail.loki | string | `""` | The URL to Loki | | addons.promtail.volumeMounts | list | `[]` | Specify a list of volumes that get mounted in the promtail container. At least 1 volumeMount is required! | -| addons.vpn | object | See values.yaml | The common chart supports adding a VPN add-on. It can be configured under this key. For more info, check out [our docs](https://bjw-s.github.io/helm-charts/docs/common-library/common-library-add-ons/#wireguard-vpn) | +| addons.vpn | object | See values.yaml | The common chart supports adding a VPN add-on. It can be configured under this key. | | addons.vpn.args | list | `[]` | Override the args for the vpn sidecar container | | addons.vpn.configFile | string | `nil` | Provide a customized vpn configuration file to be used by the VPN. | | addons.vpn.configFileSecret | string | `nil` | Reference an existing secret that contains the VPN configuration file The chart expects it to be present under the `vpnConfigfile` key. | @@ -92,26 +92,16 @@ N/A | addons.vpn.gluetun | object | See below | Gluetun specific configuration -- Make sure to read the [documentation](https://github.com/qdm12/gluetun/wiki) to see how to configure this addon! | | addons.vpn.gluetun.image.pullPolicy | string | `"IfNotPresent"` | Specify the Gluetun image pull policy | | addons.vpn.gluetun.image.repository | string | `"docker.io/qmcgaw/gluetun"` | Specify the Gluetun image | -| addons.vpn.gluetun.image.tag | string | `"v3.30.0"` | Specify the Gluetun image tag | +| addons.vpn.gluetun.image.tag | string | `"v3.31.1"` | Specify the Gluetun image tag | | addons.vpn.livenessProbe | object | `{}` | Optionally specify a livenessProbe, e.g. to check if the connection is still being protected by the VPN | | addons.vpn.networkPolicy.annotations | object | `{}` | Provide additional annotations which may be required. | | addons.vpn.networkPolicy.egress | string | `nil` | The egress configuration for your network policy, All outbound traffic from the pod will be blocked unless specified here. [[ref]](https://kubernetes.io/docs/concepts/services-networking/network-policies/) [[recipes]](https://github.com/ahmetb/kubernetes-network-policy-recipes) | | addons.vpn.networkPolicy.enabled | bool | `false` | If set to true, will deploy a network policy that blocks all outbound traffic except traffic specified as allowed | | addons.vpn.networkPolicy.labels | object | `{}` | Provide additional labels which may be required. | | addons.vpn.networkPolicy.podSelectorLabels | object | `{}` | Provide additional podSelector labels which may be required. | -| addons.vpn.openvpn | object | See below | OpenVPN specific configuration | -| addons.vpn.openvpn.auth | string | `nil` | Credentials to connect to the VPN Service (used with -a) | -| addons.vpn.openvpn.authSecret | string | `nil` | Optionally specify an existing secret that contains the credentials. Credentials should be stored under the `VPN_AUTH` key | -| addons.vpn.openvpn.image.pullPolicy | string | `"IfNotPresent"` | Specify the openvpn client image pull policy | -| addons.vpn.openvpn.image.repository | string | `"dperson/openvpn-client"` | Specify the openvpn client image | -| addons.vpn.openvpn.image.tag | string | `"latest"` | Specify the openvpn client image tag | | addons.vpn.scripts | object | See values.yaml | Provide custom up/down scripts that can be used by the vpn configuration. | | addons.vpn.securityContext | object | See values.yaml | Set the VPN container securityContext | -| addons.vpn.type | string | `"openvpn"` | Specify the VPN type. Valid options are `openvpn`, `wireguard` and `gluetun`. | -| addons.vpn.wireguard | object | See below | WireGuard specific configuration | -| addons.vpn.wireguard.image.pullPolicy | string | `"IfNotPresent"` | Specify the WireGuard image pull policy | -| addons.vpn.wireguard.image.repository | string | `"ghcr.io/k8s-at-home/wireguard"` | Specify the WireGuard image | -| addons.vpn.wireguard.image.tag | string | `"v1.0.20210914"` | Specify the WireGuard image tag | +| addons.vpn.type | string | `"gluetun"` | Specify the VPN type. Valid options are `gluetun`. | | affinity | object | `{}` | Defines affinity constraint rules. [[ref]](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) | | args | list | `[]` | Override the args for the default container | | automountServiceAccountToken | bool | `true` | Specifies whether a service account token should be automatically mounted. | @@ -210,6 +200,9 @@ N/A | service.main.ipFamilies | list | `[]` | The ip families that should be used. Options: IPv4, IPv6 | | service.main.ipFamilyPolicy | string | `nil` | Specify the ip policy. Options: SingleStack, PreferDualStack, RequireDualStack | | service.main.labels | object | `{}` | Provide additional labels which may be required. | +| service.main.monitor | object | See below | Configure a serviceMonitor for this Service. | +| service.main.monitor.enabled | bool | `false` | Enables or disables the serviceMonitor. | +| service.main.monitor.endpoints | list | See values.yaml | Configures the endpoints for the serviceMonitor. | | service.main.nameOverride | string | `nil` | Override the name suffix that is used for this service | | service.main.ports | object | See below | Configure the Service port information here. Additional ports can be added by adding a dictionary key similar to the 'http' service. | | service.main.ports.http.enabled | bool | `true` | Enables or disables the port | diff --git a/charts/library/common/README_CONFIG.md.gotmpl b/charts/library/common/README_CONFIG.md.gotmpl index 96537b70..60df7d3a 100644 --- a/charts/library/common/README_CONFIG.md.gotmpl +++ b/charts/library/common/README_CONFIG.md.gotmpl @@ -21,7 +21,7 @@ Include this chart as a dependency in your `Chart.yaml` e.g. # Chart.yaml dependencies: - name: common - version: 0.1.0 + version: {{ template "chart.version" . }} repository: {{ template "custom.helm.url" }} ``` diff --git a/charts/library/common/templates/_service.tpl b/charts/library/common/templates/_service.tpl index 35811938..4b84b685 100644 --- a/charts/library/common/templates/_service.tpl +++ b/charts/library/common/templates/_service.tpl @@ -3,7 +3,7 @@ Renders the Service objects required by the chart. */}} {{- define "common.service" -}} {{- /* Generate named services as required */ -}} - {{- range $name, $service := .Values.service }} + {{- range $name, $service := .Values.service -}} {{- if $service.enabled -}} {{- $serviceValues := $service -}} @@ -12,10 +12,24 @@ Renders the Service objects required by the chart. {{- $_ := set $serviceValues "nameOverride" $name -}} {{ end -}} + {{/* Include the Service class */}} {{- $_ := set $ "ObjectValues" (dict "service" $serviceValues) -}} - {{- include "common.classes.service" $ }} - {{- end }} - {{- end }} + {{- include "common.classes.service" $ | nindent 0 -}} + + {{/* Include a serviceMonitor if required */}} + {{- if $service.monitor.enabled | default false -}} + {{- $_ := set $ "ObjectValues" (dict "serviceMonitor" $serviceValues.monitor) -}} + {{- $_ := set $.ObjectValues.serviceMonitor "nameOverride" $serviceValues.nameOverride -}} + + {{- $serviceName := include "common.names.fullname" $ -}} + {{- if and (hasKey $serviceValues "nameOverride") $serviceValues.nameOverride -}} + {{- $serviceName = printf "%v-%v" $serviceName $serviceValues.nameOverride -}} + {{ end -}} + {{- $_ := set $.ObjectValues.serviceMonitor "serviceName" $serviceName -}} + {{- include "common.classes.serviceMonitor" $ | nindent 0 -}} + {{- end -}} + {{- end -}} + {{- end -}} {{- end }} {{/* diff --git a/charts/library/common/templates/addons/code-server/_volume.tpl b/charts/library/common/templates/addons/code-server/_volume.tpl index 6e6d5947..8e294e6e 100644 --- a/charts/library/common/templates/addons/code-server/_volume.tpl +++ b/charts/library/common/templates/addons/code-server/_volume.tpl @@ -9,7 +9,7 @@ secret: {{- else }} secretName: {{ include "common.names.fullname" . }}-deploykey {{- end }} - defaultMode: 256 + defaultMode: {{ "0400" | toDecimal }} items: - key: id_rsa path: id_rsa diff --git a/charts/library/common/templates/addons/vpn/_vpn.tpl b/charts/library/common/templates/addons/vpn/_vpn.tpl index 318ddfb3..1cebf5e5 100644 --- a/charts/library/common/templates/addons/vpn/_vpn.tpl +++ b/charts/library/common/templates/addons/vpn/_vpn.tpl @@ -5,11 +5,11 @@ It will include / inject the required templates based on the given values. {{- define "common.addon.vpn" -}} {{- if .Values.addons.vpn.enabled -}} {{- if eq "openvpn" .Values.addons.vpn.type -}} - {{- include "common.addon.openvpn" . }} + {{- fail "The 'openvpn' VPN type is no longer supported. Please migrate to the 'gluetun' type." . }} {{- end -}} {{- if eq "wireguard" .Values.addons.vpn.type -}} - {{- include "common.addon.wireguard" . }} + {{- fail "The 'wireguard' VPN type is no longer supported. Please migrate to the 'gluetun' type." . }} {{- end -}} {{- if eq "gluetun" .Values.addons.vpn.type -}} diff --git a/charts/library/common/templates/addons/vpn/openvpn/_addon.tpl b/charts/library/common/templates/addons/vpn/openvpn/_addon.tpl deleted file mode 100644 index 1cfcdd51..00000000 --- a/charts/library/common/templates/addons/vpn/openvpn/_addon.tpl +++ /dev/null @@ -1,17 +0,0 @@ -{{/* -Template to render OpenVPN addon. It will add the container to the list of additionalContainers -and add a credentials secret if speciffied. -*/}} -{{- define "common.addon.openvpn" -}} - {{/* Append the openVPN container to the additionalContainers */}} - {{- $container := include "common.addon.openvpn.container" . | fromYaml -}} - {{- if $container -}} - {{- $_ := set .Values.additionalContainers "addon-openvpn" $container -}} - {{- end -}} - - {{/* Include the secret if not empty */}} - {{- $secret := include "common.addon.openvpn.secret" . -}} - {{- if $secret -}} - {{- $secret | nindent 0 -}} - {{- end -}} -{{- end -}} diff --git a/charts/library/common/templates/addons/vpn/openvpn/_container.tpl b/charts/library/common/templates/addons/vpn/openvpn/_container.tpl deleted file mode 100644 index b2621678..00000000 --- a/charts/library/common/templates/addons/vpn/openvpn/_container.tpl +++ /dev/null @@ -1,66 +0,0 @@ -{{/* -The OpenVPN sidecar container to be inserted. -*/}} -{{- define "common.addon.openvpn.container" -}} -name: openvpn -image: "{{ .Values.addons.vpn.openvpn.image.repository }}:{{ .Values.addons.vpn.openvpn.image.tag }}" -imagePullPolicy: {{ .Values.addons.vpn.openvpn.pullPolicy }} -{{- with .Values.addons.vpn.securityContext }} -securityContext: - {{- toYaml . | nindent 2 }} -{{- end }} -{{- with .Values.addons.vpn.env }} -env: - {{- . | toYaml | nindent 2 }} -{{- end }} -{{- with .Values.addons.vpn.envFrom }} -envFrom: - {{- . | toYaml | nindent 2 }} -{{- end }} -{{- with .Values.addons.vpn.args }} -args: - {{- . | toYaml | nindent 2 }} -{{- end }} -{{- if or .Values.addons.vpn.openvpn.auth .Values.addons.vpn.openvpn.authSecret }} -envFrom: - - secretRef: - {{- if .Values.addons.vpn.openvpn.authSecret }} - name: {{ .Values.addons.vpn.openvpn.authSecret }} - {{- else }} - name: {{ include "common.names.fullname" . }}-openvpn - {{- end }} -{{- end }} -{{- if or .Values.addons.vpn.configFile .Values.addons.vpn.configFileSecret .Values.addons.vpn.scripts.up .Values.addons.vpn.scripts.down .Values.addons.vpn.additionalVolumeMounts .Values.persistence.shared.enabled }} -volumeMounts: -{{- if or .Values.addons.vpn.configFile .Values.addons.vpn.configFileSecret }} - - name: vpnconfig - mountPath: /vpn/vpn.conf - subPath: vpnConfigfile -{{- end }} -{{- if .Values.addons.vpn.scripts.up }} - - name: vpnscript - mountPath: /vpn/up.sh - subPath: up.sh -{{- end }} -{{- if .Values.addons.vpn.scripts.down }} - - name: vpnscript - mountPath: /vpn/down.sh - subPath: down.sh -{{- end }} -{{- if .Values.persistence.shared.enabled }} - - mountPath: {{ .Values.persistence.shared.mountPath }} - name: shared -{{- end }} -{{- with .Values.addons.vpn.additionalVolumeMounts }} - {{- toYaml . | nindent 2 }} -{{- end }} -{{- end }} -{{- with .Values.addons.vpn.livenessProbe }} -livenessProbe: - {{- toYaml . | nindent 2 }} -{{- end -}} -{{- with .Values.addons.vpn.resources }} -resources: - {{- toYaml . | nindent 2 }} -{{- end }} -{{- end -}} diff --git a/charts/library/common/templates/addons/vpn/openvpn/_secret.tpl b/charts/library/common/templates/addons/vpn/openvpn/_secret.tpl deleted file mode 100644 index 999699e6..00000000 --- a/charts/library/common/templates/addons/vpn/openvpn/_secret.tpl +++ /dev/null @@ -1,16 +0,0 @@ -{{/* -The OpenVPN credentials secrets to be included. -*/}} -{{- define "common.addon.openvpn.secret" -}} -{{- with .Values.addons.vpn.openvpn.auth }} ---- -apiVersion: v1 -kind: Secret -metadata: - name: {{ include "common.names.fullname" $ }}-openvpn - labels: {{- include "common.labels" $ | nindent 4 }} - annotations: {{- include "common.annotations" $ | nindent 4 }} -data: - VPN_AUTH: {{ . | b64enc }} -{{- end -}} -{{- end -}} diff --git a/charts/library/common/templates/addons/vpn/wireguard/_addon.tpl b/charts/library/common/templates/addons/vpn/wireguard/_addon.tpl deleted file mode 100644 index 6c7ea35c..00000000 --- a/charts/library/common/templates/addons/vpn/wireguard/_addon.tpl +++ /dev/null @@ -1,11 +0,0 @@ -{{/* -Template to render Wireguard addon. It will add the container to the list of additionalContainers. -*/}} -*/}} -{{- define "common.addon.wireguard" -}} - {{/* Append the Wireguard container to the additionalContainers */}} - {{- $container := fromYaml (include "common.addon.wireguard.container" .) -}} - {{- if $container -}} - {{- $_ := set .Values.additionalContainers "addon-wireguard" $container -}} - {{- end -}} -{{- end -}} diff --git a/charts/library/common/templates/addons/vpn/wireguard/_container.tpl b/charts/library/common/templates/addons/vpn/wireguard/_container.tpl deleted file mode 100644 index 7b755592..00000000 --- a/charts/library/common/templates/addons/vpn/wireguard/_container.tpl +++ /dev/null @@ -1,57 +0,0 @@ -{{/* -The Wireguard sidecar container to be inserted. -*/}} -{{- define "common.addon.wireguard.container" -}} -name: wireguard -image: "{{ .Values.addons.vpn.wireguard.image.repository }}:{{ .Values.addons.vpn.wireguard.image.tag }}" -imagePullPolicy: {{ .Values.addons.vpn.wireguard.pullPolicy }} -{{- with .Values.addons.vpn.securityContext }} -securityContext: - {{- toYaml . | nindent 2 }} -{{- end }} -{{- with .Values.addons.vpn.env }} -env: - {{- . | toYaml | nindent 2 }} -{{- end }} -{{- with .Values.addons.vpn.envFrom }} -envFrom: - {{- . | toYaml | nindent 2 }} -{{- end }} -{{- with .Values.addons.vpn.args }} -args: - {{- . | toYaml | nindent 2 }} -{{- end }} -{{- if or .Values.addons.vpn.configFile .Values.addons.vpn.configFileSecret .Values.addons.vpn.scripts.up .Values.addons.vpn.scripts.down .Values.addons.vpn.additionalVolumeMounts .Values.persistence.shared.enabled }} -volumeMounts: -{{- if or .Values.addons.vpn.configFile .Values.addons.vpn.configFileSecret }} - - name: vpnconfig - mountPath: /etc/wireguard/wg0.conf - subPath: vpnConfigfile -{{- end }} -{{- if .Values.addons.vpn.scripts.up }} - - name: vpnscript - mountPath: /config/up.sh - subPath: up.sh -{{- end }} -{{- if .Values.addons.vpn.scripts.down }} - - name: vpnscript - mountPath: /config/down.sh - subPath: down.sh -{{- end }} -{{- if .Values.persistence.shared.enabled }} - - mountPath: {{ .Values.persistence.shared.mountPath }} - name: shared -{{- end }} -{{- with .Values.addons.vpn.additionalVolumeMounts }} - {{- toYaml . | nindent 2 }} -{{- end }} -{{- end }} -{{- with .Values.addons.vpn.livenessProbe }} -livenessProbe: - {{- toYaml . | nindent 2 }} -{{- end -}} -{{- with .Values.addons.vpn.resources }} -resources: - {{- toYaml . | nindent 2 }} -{{- end }} -{{- end -}} diff --git a/charts/library/common/templates/classes/_service.tpl b/charts/library/common/templates/classes/_service.tpl index e290678f..d4386d3e 100644 --- a/charts/library/common/templates/classes/_service.tpl +++ b/charts/library/common/templates/classes/_service.tpl @@ -21,9 +21,11 @@ apiVersion: v1 kind: Service metadata: name: {{ $serviceName }} - {{- with (merge ($values.labels | default dict) (include "common.labels" $ | fromYaml)) }} - labels: {{- toYaml . | nindent 4 }} - {{- end }} + labels: + app.kubernetes.io/service: {{ $serviceName }} + {{- with (merge ($values.labels | default dict) (include "common.labels" $ | fromYaml)) }} + {{- toYaml . | nindent 4 }} + {{- end }} annotations: {{- if eq ( $primaryPort.protocol | default "" ) "HTTPS" }} traefik.ingress.kubernetes.io/service.serversscheme: https diff --git a/charts/library/common/templates/classes/_serviceMonitor.tpl b/charts/library/common/templates/classes/_serviceMonitor.tpl new file mode 100644 index 00000000..f888b18d --- /dev/null +++ b/charts/library/common/templates/classes/_serviceMonitor.tpl @@ -0,0 +1,30 @@ +{{- define "common.classes.serviceMonitor" -}} +{{- $values := dict -}} +{{- if hasKey . "ObjectValues" -}} + {{- with .ObjectValues.serviceMonitor -}} + {{- $values = . -}} + {{- end -}} +{{ end -}} + +{{- $serviceMonitorName := include "common.names.fullname" . -}} +{{- if and (hasKey $values "nameOverride") $values.nameOverride -}} + {{- $serviceMonitorName = printf "%v-%v" $serviceMonitorName $values.nameOverride -}} +{{ end -}} +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ $serviceMonitorName }} + {{- with (merge ($values.labels | default dict) (include "common.labels" $ | fromYaml)) }} + labels: {{- toYaml . | nindent 4 }} + {{- end }} + {{- with (merge ($values.annotations | default dict) (include "common.annotations" $ | fromYaml)) }} + annotations: {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + app.kubernetes.io/service: {{ $values.serviceName }} + {{- include "common.labels.selectorLabels" . | nindent 6 }} + endpoints: {{- toYaml (required (printf "endpoints are required for serviceMonitor %v" $serviceMonitorName) $values.endpoints) | nindent 4 }} +{{- end }} diff --git a/charts/library/common/templates/lib/chart/_annotations.tpl b/charts/library/common/templates/lib/chart/_annotations.tpl index 2c64a36b..d3b661d8 100644 --- a/charts/library/common/templates/lib/chart/_annotations.tpl +++ b/charts/library/common/templates/lib/chart/_annotations.tpl @@ -15,13 +15,13 @@ {{- tpl (toYaml .Values.podAnnotations) . | nindent 0 -}} {{- end -}} - {{- $configMapsFound := false -}} + {{- $configMapsFound := dict -}} {{- range $name, $configmap := .Values.configmap -}} {{- if $configmap.enabled -}} - {{- $configMapsFound = true -}} + {{- $_ := set $configMapsFound $name (toYaml $configmap.data | sha256sum) -}} {{- end -}} {{- end -}} {{- if $configMapsFound -}} - {{- printf "checksum/config: %v" (include ("common.configmap") . | sha256sum) | nindent 0 -}} + {{- printf "checksum/config: %v" (toYaml $configMapsFound | sha256sum) | nindent 0 -}} {{- end -}} {{- end -}} diff --git a/charts/library/common/values.yaml b/charts/library/common/values.yaml index bbe2ff53..868f6720 100644 --- a/charts/library/common/values.yaml +++ b/charts/library/common/values.yaml @@ -277,6 +277,20 @@ service: # -- Provide additional labels which may be required. labels: {} + # -- Configure a serviceMonitor for this Service. + # @default -- See below + monitor: + # -- Enables or disables the serviceMonitor. + enabled: false + # -- Configures the endpoints for the serviceMonitor. + # @default -- See values.yaml + endpoints: + - port: http + scheme: http + path: /metrics + interval: 1m + scrapeTimeout: 10s + # -- Configure the Service port information here. # Additional ports can be added by adding a dictionary key similar to the 'http' service. # @default -- See below @@ -477,42 +491,13 @@ resources: {} addons: # -- The common chart supports adding a VPN add-on. It can be configured under this key. - # For more info, check out [our docs](https://bjw-s.github.io/helm-charts/docs/common-library/common-library-add-ons/#wireguard-vpn) # @default -- See values.yaml vpn: # -- Enable running a VPN in the pod to route traffic through a VPN enabled: false - # -- Specify the VPN type. Valid options are `openvpn`, `wireguard` and `gluetun`. - type: openvpn - - # -- OpenVPN specific configuration - # @default -- See below - openvpn: - image: - # -- Specify the openvpn client image - repository: dperson/openvpn-client - # -- Specify the openvpn client image tag - tag: latest - # -- Specify the openvpn client image pull policy - pullPolicy: IfNotPresent - - # -- Credentials to connect to the VPN Service (used with -a) - auth: # "user;password" - # -- Optionally specify an existing secret that contains the credentials. - # Credentials should be stored under the `VPN_AUTH` key - authSecret: # my-vpn-secret - - # -- WireGuard specific configuration - # @default -- See below - wireguard: - image: - # -- Specify the WireGuard image - repository: ghcr.io/k8s-at-home/wireguard - # -- Specify the WireGuard image tag - tag: v1.0.20210914 - # -- Specify the WireGuard image pull policy - pullPolicy: IfNotPresent + # -- Specify the VPN type. Valid options are `gluetun`. + type: gluetun # -- Gluetun specific configuration # -- Make sure to read the [documentation](https://github.com/qdm12/gluetun/wiki) to see how to configure this addon! @@ -522,7 +507,7 @@ addons: # -- Specify the Gluetun image repository: docker.io/qmcgaw/gluetun # -- Specify the Gluetun image tag - tag: v3.30.0 + tag: v3.31.1 # -- Specify the Gluetun image pull policy pullPolicy: IfNotPresent @@ -619,7 +604,7 @@ addons: # -- Specify the code-server image repository: ghcr.io/coder/code-server # -- Specify the code-server image tag - tag: 4.5.1 + tag: 4.7.0 # -- Specify the code-server image pull policy pullPolicy: IfNotPresent diff --git a/charts/other/app-template/Chart.yaml b/charts/other/app-template/Chart.yaml index de544cdf..3c9cf913 100644 --- a/charts/other/app-template/Chart.yaml +++ b/charts/other/app-template/Chart.yaml @@ -2,15 +2,19 @@ apiVersion: v2 description: A common powered chart template. This can be useful for small projects that don't have their own chart. name: app-template -version: 0.1.1 +version: 0.2.0 maintainers: - name: bjw-s email: me@bjw-s.dev dependencies: - name: common repository: https://bjw-s.github.io/helm-charts - version: 0.1.0 + version: 0.2.0 annotations: artifacthub.io/changes: |- - - kind: added - description: Automatically set `global.nameOverride` to release name + - kind: changed + description: | + **BREAKING** Updated library version to 0.2.0. + links: + - name: Common library chart definition + url: https://github.com/bjw-s/helm-charts/blob/main/charts/library/common/Chart.yaml diff --git a/charts/other/app-template/tests/addons/vpn_test.yaml b/charts/other/app-template/tests/addons/vpn_test.yaml index 07bd51a0..20083d78 100644 --- a/charts/other/app-template/tests/addons/vpn_test.yaml +++ b/charts/other/app-template/tests/addons/vpn_test.yaml @@ -35,7 +35,7 @@ tests: - documentIndex: *DeploymentDocument equal: path: spec.template.spec.containers[1].name - value: openvpn + value: gluetun - it: addon enabled with configFile should pass set: @@ -56,7 +56,7 @@ tests: contains: path: spec.template.spec.containers[1].volumeMounts content: - mountPath: /vpn/vpn.conf + mountPath: /gluetun/config.conf name: vpnconfig subPath: vpnConfigfile - documentIndex: *DeploymentDocument @@ -90,7 +90,7 @@ tests: contains: path: spec.template.spec.containers[1].volumeMounts content: - mountPath: /vpn/vpn.conf + mountPath: /gluetun/config.conf name: vpnconfig subPath: vpnConfigfile - documentIndex: *DeploymentDocument @@ -104,32 +104,6 @@ tests: path: vpnConfigfile secretName: test-secret - - it: addon enabled with managed secret should pass - set: - addons: - vpn: - enabled: true - openvpn: - auth: | - - asserts: - - hasDocuments: - count: 3 - - documentIndex: &SecretDocument 0 - isKind: - of: Secret - - documentIndex: &DeploymentDocument 1 - isKind: - of: Deployment - - documentIndex: 2 - isKind: - of: Service - - documentIndex: *DeploymentDocument - not: true - equal: - path: spec.template.spec.containers[0].name - value: vpn - - it: addon enabled with custom env vars dict should pass set: addons: diff --git a/charts/other/app-template/tests/configmap/metadata_test.yaml b/charts/other/app-template/tests/configmap/metadata_test.yaml index 8e8a5c92..5513961f 100644 --- a/charts/other/app-template/tests/configmap/metadata_test.yaml +++ b/charts/other/app-template/tests/configmap/metadata_test.yaml @@ -21,7 +21,7 @@ tests: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME - helm.sh/chart: app-template-0.1.1 + helm.sh/chart: app-template-0.2.0 - it: custom metadata should pass set: @@ -48,7 +48,7 @@ tests: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME - helm.sh/chart: app-template-0.1.1 + helm.sh/chart: app-template-0.2.0 test_label: test - it: custom metadata with global metadata should pass @@ -83,5 +83,5 @@ tests: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME global_label: test - helm.sh/chart: app-template-0.1.1 + helm.sh/chart: app-template-0.2.0 test_label: test diff --git a/charts/other/app-template/tests/configmap/pod_metadata_test.yaml b/charts/other/app-template/tests/configmap/pod_metadata_test.yaml index f4bb1f42..593baa89 100644 --- a/charts/other/app-template/tests/configmap/pod_metadata_test.yaml +++ b/charts/other/app-template/tests/configmap/pod_metadata_test.yaml @@ -7,6 +7,8 @@ tests: configmap: config: enabled: true + data: + test: value 1 asserts: - documentIndex: &ControllerDoc 1 isKind: @@ -15,4 +17,4 @@ tests: equal: path: spec.template.metadata.annotations value: - checksum/config: df9353f1f56b09774c9982674bda7fde05cde68ad7bcbf9594a66f3610bce501 + checksum/config: afdf20f511621d5cb358c5e8b0da2d14cdbe2549fb12fa123f6a6e9baabef26e diff --git a/charts/other/app-template/tests/controller/metadata_daemonset_test.yaml b/charts/other/app-template/tests/controller/metadata_daemonset_test.yaml index ccf9822d..36b9bc82 100644 --- a/charts/other/app-template/tests/controller/metadata_daemonset_test.yaml +++ b/charts/other/app-template/tests/controller/metadata_daemonset_test.yaml @@ -19,7 +19,7 @@ tests: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME - helm.sh/chart: app-template-0.1.1 + helm.sh/chart: app-template-0.2.0 - it: custom metadata should pass set: @@ -45,7 +45,7 @@ tests: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME - helm.sh/chart: app-template-0.1.1 + helm.sh/chart: app-template-0.2.0 test_label: test - it: custom metadata with global metadata should pass @@ -79,5 +79,5 @@ tests: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME global_label: test - helm.sh/chart: app-template-0.1.1 + helm.sh/chart: app-template-0.2.0 test_label: test diff --git a/charts/other/app-template/tests/controller/metadata_deployment_test.yaml b/charts/other/app-template/tests/controller/metadata_deployment_test.yaml index 4a7819e5..18470c60 100644 --- a/charts/other/app-template/tests/controller/metadata_deployment_test.yaml +++ b/charts/other/app-template/tests/controller/metadata_deployment_test.yaml @@ -19,7 +19,7 @@ tests: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME - helm.sh/chart: app-template-0.1.1 + helm.sh/chart: app-template-0.2.0 - it: custom metadata should pass set: @@ -45,7 +45,7 @@ tests: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME - helm.sh/chart: app-template-0.1.1 + helm.sh/chart: app-template-0.2.0 test_label: test - it: custom metadata with global metadata should pass @@ -79,5 +79,5 @@ tests: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME global_label: test - helm.sh/chart: app-template-0.1.1 + helm.sh/chart: app-template-0.2.0 test_label: test diff --git a/charts/other/app-template/tests/controller/metadata_statefulset_test.yaml b/charts/other/app-template/tests/controller/metadata_statefulset_test.yaml index 591d1b59..b70dcb97 100644 --- a/charts/other/app-template/tests/controller/metadata_statefulset_test.yaml +++ b/charts/other/app-template/tests/controller/metadata_statefulset_test.yaml @@ -19,7 +19,7 @@ tests: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME - helm.sh/chart: app-template-0.1.1 + helm.sh/chart: app-template-0.2.0 - it: custom metadata should pass set: @@ -45,7 +45,7 @@ tests: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME - helm.sh/chart: app-template-0.1.1 + helm.sh/chart: app-template-0.2.0 test_label: test - it: custom metadata with global metadata should pass @@ -79,5 +79,5 @@ tests: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME global_label: test - helm.sh/chart: app-template-0.1.1 + helm.sh/chart: app-template-0.2.0 test_label: test diff --git a/charts/other/app-template/tests/ingress/metadata_test.yaml b/charts/other/app-template/tests/ingress/metadata_test.yaml index 0f0f2654..06ead2f3 100644 --- a/charts/other/app-template/tests/ingress/metadata_test.yaml +++ b/charts/other/app-template/tests/ingress/metadata_test.yaml @@ -19,7 +19,7 @@ tests: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME - helm.sh/chart: app-template-0.1.1 + helm.sh/chart: app-template-0.2.0 - it: custom metadata should pass set: @@ -45,7 +45,7 @@ tests: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME - helm.sh/chart: app-template-0.1.1 + helm.sh/chart: app-template-0.2.0 test_label: test - it: custom metadata with global metadata should pass @@ -79,5 +79,5 @@ tests: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME global_label: test - helm.sh/chart: app-template-0.1.1 + helm.sh/chart: app-template-0.2.0 test_label: test diff --git a/charts/other/app-template/tests/pvc/metadata_test.yaml b/charts/other/app-template/tests/pvc/metadata_test.yaml index 967f81aa..dc8ab371 100644 --- a/charts/other/app-template/tests/pvc/metadata_test.yaml +++ b/charts/other/app-template/tests/pvc/metadata_test.yaml @@ -19,7 +19,7 @@ tests: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME - helm.sh/chart: app-template-0.1.1 + helm.sh/chart: app-template-0.2.0 - it: retain enabled should pass set: @@ -42,7 +42,7 @@ tests: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME - helm.sh/chart: app-template-0.1.1 + helm.sh/chart: app-template-0.2.0 - it: custom metadata should pass set: @@ -68,7 +68,7 @@ tests: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME - helm.sh/chart: app-template-0.1.1 + helm.sh/chart: app-template-0.2.0 test_label: test - it: custom metadata with global metadata should pass @@ -102,5 +102,5 @@ tests: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME global_label: test - helm.sh/chart: app-template-0.1.1 + helm.sh/chart: app-template-0.2.0 test_label: test diff --git a/charts/other/app-template/tests/service/metadata_test.yaml b/charts/other/app-template/tests/service/metadata_test.yaml index 06394113..189940fa 100644 --- a/charts/other/app-template/tests/service/metadata_test.yaml +++ b/charts/other/app-template/tests/service/metadata_test.yaml @@ -17,7 +17,8 @@ tests: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME - helm.sh/chart: app-template-0.1.1 + app.kubernetes.io/service: RELEASE-NAME + helm.sh/chart: app-template-0.2.0 - it: custom metadata should pass set: @@ -43,7 +44,8 @@ tests: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME - helm.sh/chart: app-template-0.1.1 + app.kubernetes.io/service: RELEASE-NAME + helm.sh/chart: app-template-0.2.0 test_label: test - it: custom metadata with global metadata should pass @@ -76,6 +78,7 @@ tests: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: RELEASE-NAME + app.kubernetes.io/service: RELEASE-NAME global_label: test - helm.sh/chart: app-template-0.1.1 + helm.sh/chart: app-template-0.2.0 test_label: test diff --git a/charts/other/app-template/tests/service/servicemonitor_test.yaml b/charts/other/app-template/tests/service/servicemonitor_test.yaml new file mode 100644 index 00000000..fcbcdcf2 --- /dev/null +++ b/charts/other/app-template/tests/service/servicemonitor_test.yaml @@ -0,0 +1,109 @@ +suite: service values +templates: + - common.yaml +tests: + - it: a serviceMonitor is not created by default + asserts: + - hasDocuments: + count: 2 + - documentIndex: &ServiceDocument 0 + isKind: + of: Deployment + - documentIndex: &ServiceDocument 1 + isKind: + of: Service + + - it: a serviceMonitor is not created when disabled + set: + service: + main: + monitor: + enabled: false + asserts: + - hasDocuments: + count: 2 + - documentIndex: &ServiceDocument 0 + isKind: + of: Deployment + - documentIndex: &ServiceDocument 1 + isKind: + of: Service + + - it: a serviceMonitor is created + set: + service: + main: + monitor: + enabled: true + endpoints: + - port: http + scheme: http + path: /metrics + interval: 1m + scrapeTimeout: 10s + asserts: + - hasDocuments: + count: 3 + - documentIndex: &ServiceMonitorDocument 2 + isKind: + of: ServiceMonitor + - documentIndex: *ServiceMonitorDocument + equal: + path: metadata.name + value: RELEASE-NAME + - documentIndex: *ServiceMonitorDocument + equal: + path: spec.selector.matchLabels + value: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/name: RELEASE-NAME + app.kubernetes.io/service: RELEASE-NAME + - documentIndex: *ServiceMonitorDocument + equal: + path: spec.endpoints + value: + - port: http + scheme: http + path: /metrics + interval: 1m + scrapeTimeout: 10s + + - it: a serviceMonitor is created with nameOverride + set: + service: + main: + nameOverride: test + monitor: + enabled: true + endpoints: + - port: http + scheme: http + path: /metrics + interval: 1m + scrapeTimeout: 10s + asserts: + - hasDocuments: + count: 3 + - documentIndex: &ServiceMonitorDocument 2 + isKind: + of: ServiceMonitor + - documentIndex: *ServiceMonitorDocument + equal: + path: metadata.name + value: RELEASE-NAME-test + - documentIndex: *ServiceMonitorDocument + equal: + path: spec.selector.matchLabels + value: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/name: RELEASE-NAME + app.kubernetes.io/service: RELEASE-NAME-test + - documentIndex: *ServiceMonitorDocument + equal: + path: spec.endpoints + value: + - port: http + scheme: http + path: /metrics + interval: 1m + scrapeTimeout: 10s diff --git a/docs/common-library/common-library-add-ons.md b/docs/common-library/common-library-add-ons.md index 2ca78626..8fc67984 100644 --- a/docs/common-library/common-library-add-ons.md +++ b/docs/common-library/common-library-add-ons.md @@ -50,222 +50,3 @@ addons: - name: config mountPath: /config ``` - -## Wireguard VPN - -The Wireguard add-on enables you to force all (or selected) network traffic -through a VPN. - -This example shows how to add a Wireguard sidecar to our -[qBittorrent Helm chart](https://github.com/k8s-at-home/charts/tree/master/charts/stable/qbittorrent). -It does not cover all of the configuration possibilities of the -[Wireguard client image](https://github.com/k8s-at-home/container-images/tree/main/apps/wireguard), -but should give a good starting point for configuring a similar setup. - -### Example values - -Below is an annotated example `values.yaml` that will result in a qBittorrent -container with **all** its traffic routed through a VPN. In order to have -functioning ingress and/or probes, it might be required to open certain -networks or ports on the VPN firewall. That is beyond the scope of this -document. Please refer to the -[Wireguard client image](https://github.com/k8s-at-home/container-images/tree/main/apps/wireguard) -for more details on these environment variables. - -!!! note - The `WAIT_FOR_VPN` environment variable is specifically implemented by our - own qBittorrent image, and it will not work with other container images. - -```yaml -image: - repository: k8sathome/qbittorrent - tag: v4.3.3 - pullPolicy: IfNotPresent - -env: - # Our qBittorrent image has a feature that can wait for the VPN to be connected before actually starting the application. - # It does this by checking the contents of a file /shared/vpnstatus to contain the string 'connected'. - WAIT_FOR_VPN: "true" - -persistence: - config: - enabled: true - type: emptyDir - mountPath: /config - - # This should be enabled so that both the qBittorrent and Wireguard container have access to a shared volume mounted to /shared. - # It will be used to communicate between the two containers. - shared: - enabled: true - type: emptyDir - mountPath: /shared - -addons: - vpn: - enabled: true - # This Should be set to `wireguard`. This will set the add-on to use the default settings for Wireguard based connections. - type: wireguard - - # If the podSecurityContext is set to run as a different user, make sure to run the Wireguard container as UID/GID 568. - # This is required for it to be able to read certain configuration files. - securityContext: - runAsUser: 568 - runAsGroup: 568 - - env: - # Enable a killswitch that kills all trafic when the VPN is not connected - KILLSWITCH: "true" - - # The wireguard configuration file provided by your VPN provider goes here. - # - # Set AllowedIPs to 0.0.0.0/0 to route all traffic through the VPN. - # - # Pay close attention to the PostUp and PreDown lines. They must be added if you wish to run a script when the connection - # is opened / closed. - configFile: |- - [Interface] - PrivateKey = - Address = - DNS = - PostUp = /config/up.sh %i - PreDown = /config/down.sh %i - - [Peer] - PublicKey = - AllowedIPs = 0.0.0.0/0 - Endpoint = - - # The scripts that get run when the VPN connection opens/closes are defined here. - # The default scripts will write a string to represent the current connection state to a file. - # Our qBittorrent image has a feature that can wait for this file to contain the word 'connected' before actually starting the application. - scripts: - up: |- - #!/bin/bash - echo "connected" > /shared/vpnstatus - - down: |- - #!/bin/bash - echo "disconnected" > /shared/vpnstatus -``` - -## OpenVPN - -Similar to the Wireguard VPN, the OpenVPN add-on enables you to force all -(or selected) network traffic through a VPN. - -This example shows how to add an OpenVPN sidecar to our -[qBittorrent Helm chart](https://github.com/k8s-at-home/charts/tree/master/charts/stable/qbittorrent). -It does not cover all of the configuration possibilities of the -[OpenVPN client image](https://github.com/dperson/openvpn-client) by -[@dperson](https://github.com/dperson), but should give a good starting point -for configuring a similar setup. - -### Example values - -Below is an annotated example `values.yaml` that will result in a qBittorrent -container with **all** its traffic routed through a VPN. In order to have -functioning ingress and/or probes, it might be required to open certain -networks or ports on the VPN firewall. That is beyond the scope of this -document. Please refer to the -[OpenVPN client image](https://github.com/dperson/openvpn-client) for -more details on these environment variables. - -!!! note - The `WAIT_FOR_VPN` environment variable is specifically implemented by our - own qBittorrent image, and it will not work with other container images. - -```yaml -image: - repository: k8sathome/qbittorrent - tag: v4.3.3 - pullPolicy: IfNotPresent - -env: - # Our qBittorrent image has a feature that can wait for the VPN to be - # connected before actually starting the application. - # It does this by checking the contents of a file /shared/vpnstatus to - # contain the string 'connected'. - WAIT_FOR_VPN: "true" - -persistence: - config: - enabled: true - type: emptyDir - mountPath: /config - - # This should be enabled so that both the qBittorrent and OpenVPN container have access to a shared volume mounted to /shared. - # It will be used to communicate between the two containers. - shared: - enabled: true - type: emptyDir - mountPath: /shared - -addons: - vpn: - enabled: true - # This Should be set to `openvpn`. This will set the add-on to use the default settings for OpenVPN based connections. - type: openvpn - - openvpn: - # This gets read by the Helm chart. The default OpenVPN image reads this and uses it to connect to the VPN provider. - auth: | - myuser - mypassword - - # If the podSecurityContext is set to run as a different user, make sure to run the OpenVPN container as root. - # This is required for it to be able to read certain configuration files. - securityContext: - runAsGroup: 0 - runAsUser: 0 - - env: - # Set this environment variable to 'on' to make sure all traffic gets routed through the VPN container. - # Make sure to check the other environment variables for the OpenVPN image to see how you can exclude certain - # traffic from these firewall rules. - FIREWALL: 'on' - - # The .ovpn file provided by your VPN provider goes here. - # - # Any CA / certificate must either be placed inline, or provided through an additionalVolumeMount so that OpenVPN can find it. - # - # Pay close attention to the last 3 lines in this file. They must be added if you wish to run a script when the connection - # is opened / closed. - configFile: |- - client - dev tun - proto udp - remote my-awesome-vpn-provider.com 995 - remote-cert-tls server - resolv-retry infinite - nobind - tls-version-min 1.2 - cipher AES-128-GCM - compress - ncp-disable - tun-mtu-extra 32 - auth-user-pass - - - -----BEGIN CERTIFICATE----- - MIIDMTCCAhmgAwIBAgIJAKnGGJK6qLqSMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV - -----END CERTIFICATE----- - - - script-security 2 - up /vpn/up.sh - down /vpn/down.sh - - # The scripts that get run when the VPN connection opens/closes are defined here. - # The default scripts will write a string to represent the current connection state to a file. - # Our qBittorrent image has a feature that can wait for this file to contain the word 'connected' before actually starting the application. - scripts: - up: |- - #!/bin/bash - /etc/openvpn/up.sh - echo "connected" > /shared/vpnstatus - - down: |- - #!/bin/bash - /etc/openvpn/down.sh - echo "disconnected" > /shared/vpnstatus -```