diff --git a/library/common-test/tests/pod/volume_emptyDIr_test.yaml b/library/common-test/tests/pod/volume_emptyDIr_test.yaml new file mode 100644 index 0000000000..fd9b3853c0 --- /dev/null +++ b/library/common-test/tests/pod/volume_emptyDIr_test.yaml @@ -0,0 +1,123 @@ +suite: pod emptyDir volume test +templates: + - common.yaml +tests: + - it: should pass with emptyDir volume + set: + some_medium: Memory + some_size: 2Gi + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + emptyDir-vol: + enabled: true + type: emptyDir + medium: "{{ .Values.some_medium }}" + size: "{{ .Values.some_size }}" + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.volumes + content: + name: emptyDir-vol + emptyDir: + medium: Memory + sizeLimit: 2Gi + + - it: should pass with emptyDir volume bare bones + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + emptyDir-vol: + enabled: true + type: emptyDir + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.volumes + content: + name: emptyDir-vol + emptyDir: {} + + - it: should pass with emptyDir volume with medium set + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + emptyDir-vol: + enabled: true + type: emptyDir + medium: Memory + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.volumes + content: + name: emptyDir-vol + emptyDir: + medium: Memory + + - it: should pass with emptyDir volume with size set + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + emptyDir-vol: + enabled: true + type: emptyDir + size: 3Gi + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.volumes + content: + name: emptyDir-vol + emptyDir: + sizeLimit: 3Gi + +# Failures + - it: should fail with invalid medium in emptyDir + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: emptyDir + medium: not-a-valid-medium + asserts: + - failedTemplate: + errorMessage: Persistence - Expected [medium] to be one of ["", Memory], but got [not-a-valid-medium] on emptyDir type diff --git a/library/common-test/tests/pod/volume_nfs_test.yaml b/library/common-test/tests/pod/volume_nfs_test.yaml new file mode 100644 index 0000000000..b70b1298ad --- /dev/null +++ b/library/common-test/tests/pod/volume_nfs_test.yaml @@ -0,0 +1,85 @@ +suite: pod nfs volume test +templates: + - common.yaml +tests: + - it: should pass with nfs volume + set: + some_path: /some-path + some_server: some-server + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + nfs-vol: + enabled: true + type: nfs + path: "{{ .Values.some_path }}" + server: "{{ .Values.some_server }}" + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.volumes + content: + name: nfs-vol + nfs: + path: /some-path + server: some-server + +# Failures + - it: should fail without path in nfs + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: nfs + path: "" + asserts: + - failedTemplate: + errorMessage: Persistence - Expected non-empty [path] on NFS type + + - it: should fail with path not starting with / in nfs + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: nfs + path: some-relative-path + asserts: + - failedTemplate: + errorMessage: Persistence - Expected [path] to start with a forward slash [/] on NFS type + + - it: should fail without server in nfs + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: nfs + path: /some-path + server: "" + asserts: + - failedTemplate: + errorMessage: Persistence - Expected non-empty [server] on NFS type diff --git a/library/common/1.0.0/docs/persistence.md b/library/common/1.0.0/docs/persistence.md index c11153a56a..79a44513cf 100644 --- a/library/common/1.0.0/docs/persistence.md +++ b/library/common/1.0.0/docs/persistence.md @@ -1,20 +1,28 @@ # Persistence -| Key | Type | Required | Helm Template | Default | Description | -| :----------------------------------------- | :-----------: | :------: | :----------------: | :----------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------- | -| persistence | `dict` | ❌ | ❌ | `{}` | Define the persistence as dicts | -| persistence.[volume-name] | `dict` | ✅ | ❌ | `{}` | Holds persistence definition | -| persistence.[volume-name].enabled | `boolean` | ✅ | ❌ | `false` | Enables or Disables the persistence | -| persistence.[volume-name].labels | `dict` | ❌ | ✅ (On value only) | `{}` | Additional labels for persistence | -| persistence.[volume-name].annotations | `dict` | ❌ | ✅ (On value only) | `{}` | Additional annotations for persistence | -| persistence.[volume-name].type | `string` | ❌ | ❌ | `pvc` | Define the persistence type (pvc, ixVolume, nfs, hostPath, configmap, secret) | -| persistence.[volume-name].retain | `boolean` | ❌ | ❌ | `{{ .Values.global.fallbackDefaults.pvcRetain }}` | Define wether the to add helm annotation to retain resource on uninstall (Middleware should also retain it when deleting the NS) | -| persistence.[volume-name].accessModes | `string/list` | ❌ | ✅ | `{{ .Values.global.fallbackDefaults.pvcAccessModes }}` | Define the accessModes of the PVC, if it's single can be defined as a string, multiple as a list | -| persistence.[volume-name].size | `string` | ❌ | ✅ | `{{ .Values.global.fallbackDefaults.pvcSize }}` | Define the size of the PVC, or the sizeLimit of the emptyDir (Default does not apply there) | -| persistence.[volume-name].volumeName | `string` | ❌ | ✅ | | Define the volumeName of a PV, backing the claim | -| persistence.[volume-name].existingClaim | `string` | ❌ | ✅ | | Define an existing claim to use | -| persistence.[volume-name].storageClassName | `string` | ❌ | ✅ | See `templates/lib/storage/_storageClassName.tpl` | Define an existing claim to use | -| persistence.[volume-name].targetSelectAll | `boolean` | ❌ | ❌ | `false` | Define wether to define this volume to all workloads and mount it on all containers | +| Key | Type | Required | Helm Template | Default | Description | +| :----------------------------------------------------------------------------- | :-----------: | :-------------: | :----------------: | :-----------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------- | +| persistence | `dict` | ❌ | ❌ | `{}` | Define the persistence as dicts | +| persistence.[volume-name] | `dict` | ✅ | ❌ | `{}` | Holds persistence definition | +| persistence.[volume-name].enabled | `boolean` | ✅ | ❌ | `false` | Enables or Disables the persistence | +| persistence.[volume-name].labels | `dict` | ❌ | ✅ (On value only) | `{}` | Additional labels for persistence | +| persistence.[volume-name].annotations | `dict` | ❌ | ✅ (On value only) | `{}` | Additional annotations for persistence | +| persistence.[volume-name].type | `string` | ❌ | ❌ | `pvc` | Define the persistence type (pvc, ixVolume, nfs, hostPath, configmap, secret) | +| persistence.[volume-name].retain | `boolean` | ❌ | ❌ | `{{ .Values.global.fallbackDefaults.pvcRetain }}` | Define wether the to add helm annotation to retain resource on uninstall (Middleware should also retain it when deleting the NS) | +| persistence.[volume-name].accessModes | `string/list` | ❌ | ✅ | `{{ .Values.global.fallbackDefaults.pvcAccessModes }}` | Define the accessModes of the PVC, if it's single can be defined as a string, multiple as a list | +| persistence.[volume-name].size | `string` | ❌ | ✅ | pvc: `{{ .Values.global.fallbackDefaults.pvcSize }}` emptyDir: `""` | Define the size of the PVC, or the sizeLimit of the emptyDir | +| persistence.[volume-name].volumeName | `string` | ❌ | ✅ | | Define the volumeName of a PV, backing the claim | +| persistence.[volume-name].existingClaim | `string` | ❌ | ✅ | | Define an existing claim to use | +| persistence.[volume-name].storageClassName | `string` | ❌ | ✅ | See `templates/lib/storage/_storageClassName.tpl` | Define an existing claim to use | +| persistence.[volume-name].targetSelectAll | `boolean` | ❌ | ❌ | `false` | Define wether to define this volume to all workloads and mount it on all containers | +| persistence.[volume-name].targetSelector | `dict` | ❌ | ❌ | `{}` | Define a dict with pod and containers to mount | +| persistence.[volume-name].mountPath | `string` | ❌ | ✅ | `""` | Default mountPath for all container | +| persistence.[volume-name].path | `string` | ✅(On nfs type) | ✅ | `""` | Define the nfs export share path | +| persistence.[volume-name].server | `string` | ✅(On nfs type) | ✅ | `""` | Define the nfs server | +| persistence.[volume-name].medium | `string` | ❌ | ✅ | `""` | Define the medium of emptyDir (Memory, "") | +| persistence.[volume-name].targetSelector.[pod-name] | `dict` | ❌ | ❌ | `{}` | Define a dict named after the pod to define the volume | +| persistence.[volume-name].targetSelector.[pod-name].[container-name] | `dict` | ❌ | ❌ | `{}` | Define a dict named after the container to mount the volume | +| persistence.[volume-name].targetSelector.[pod-name].[container-name].mountPath | `string` | ❌ | ✅ | `[volume-name].mountPath` | Define the mountPath for the container | --- @@ -46,5 +54,15 @@ persistence: existingClaim: existing-claim-name retain: true size: 2Gi - targetSelectAll: true + # targetSelectAll: true + targetSelector: + pod-name: + container-name: + mountPath: /path/to/mount + + nfs-vol: + enabled: true + type: nfs + path: /path/of/nfs/share + server: nfs-server ``` diff --git a/library/common/1.0.0/templates/lib/pod/_volumes.tpl b/library/common/1.0.0/templates/lib/pod/_volumes.tpl new file mode 100644 index 0000000000..807722ae12 --- /dev/null +++ b/library/common/1.0.0/templates/lib/pod/_volumes.tpl @@ -0,0 +1,55 @@ +{{/* Returns Volumes */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.pod.volumes" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the template. It is used to access the global context. +objectData: The object data to be used to render the Pod. +*/}} +{{- define "ix.v1.common.lib.pod.volumes" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- range $name, $persistenceValues := $rootCtx.Values.persistence -}} + {{- if $persistenceValues.enabled -}} + {{- $persistence := (mustDeepCopy $persistenceValues) -}} + {{- $_ := set $persistence "shortName" $name -}} + + {{- $selected := false -}} + + {{/* If set to true, define volume */}} + {{- if $persistence.targetSelectAll -}} + {{- $selected = true -}} + + {{/* If targetSelector is set, check if pod is selected */}} + {{- else if $persistence.targetSelector -}} + {{- if (mustHas $objectData.shortName (keys $persistence.targetSelector)) -}} + {{- $selected = true -}} + {{- end -}} + + {{/* If no targetSelector is set or targetSelectAll, check if pod is primary */}} + {{- else -}} + {{- if $objectData.primary -}} + {{- $selected = true -}} + {{- end -}} + {{- end -}} + + {{/* If pod selected */}} + {{- if $selected -}} + {{/* Define the volume based on type */}} + {{- $type := ($persistence.type | default $rootCtx.Values.fallbackDefaults.persistenceType) -}} + + {{- if eq "pvc" $type -}} + {{- else if eq "ixVolume" $type -}} + {{- else if eq "hostPath" $type -}} + {{- else if eq "secret" $type -}} + {{- else if eq "configmap" $type -}} + {{- else if eq "emptyDir" $type -}} + {{- include "ix.v1.common.lib.pod.volume.emptyDir" (dict "rootCtx" $rootCtx "objectData" $persistence) | trim | nindent 0 -}} + {{- else if eq "nfs" $type -}} + {{- include "ix.v1.common.lib.pod.volume.nfs" (dict "rootCtx" $rootCtx "objectData" $persistence) | trim | nindent 0 -}} + {{- end -}} + + {{- end -}} + + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/pod/volumes/_emptyDir.tpl b/library/common/1.0.0/templates/lib/pod/volumes/_emptyDir.tpl new file mode 100644 index 0000000000..72525246eb --- /dev/null +++ b/library/common/1.0.0/templates/lib/pod/volumes/_emptyDir.tpl @@ -0,0 +1,35 @@ +{{/* Returns emptyDir Volume */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.pod.volume.emptyDir" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the template. It is used to access the global context. +objectData: The object data to be used to render the volume. +*/}} +{{- define "ix.v1.common.lib.pod.volume.emptyDir" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $medium := "" -}} + {{- $size := "" -}} + {{- with $objectData.medium -}} + {{- $medium = tpl . $rootCtx -}} + {{- end -}} + {{- with $objectData.size -}} + {{- $size = tpl . $rootCtx -}} + {{- end -}} + + {{- if and $medium (ne $medium "Memory") -}} + {{- fail (printf "Persistence - Expected [medium] to be one of [\"\", Memory], but got [%s] on emptyDir type" $medium) -}} + {{- end }} +- name: {{ $objectData.shortName }} + {{- if or $medium $size }} + emptyDir: + {{- if $medium }} + medium: {{ $medium }} + {{- end -}} + {{- if $size }} + sizeLimit: {{ $size }} + {{- end -}} + {{- else }} + emptyDir: {} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/pod/volumes/_nfs.tpl b/library/common/1.0.0/templates/lib/pod/volumes/_nfs.tpl new file mode 100644 index 0000000000..0f806ff04b --- /dev/null +++ b/library/common/1.0.0/templates/lib/pod/volumes/_nfs.tpl @@ -0,0 +1,27 @@ +{{/* Returns NFS Volume */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.pod.volume.nfs" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the template. It is used to access the global context. +objectData: The object data to be used to render the volume. +*/}} +{{- define "ix.v1.common.lib.pod.volume.nfs" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- if not $objectData.path -}} + {{- fail "Persistence - Expected non-empty [path] on NFS type" -}} + {{- end -}} + + {{- $path := tpl $objectData.path $rootCtx -}} + {{- if not (hasPrefix "/" $path) -}} + {{- fail "Persistence - Expected [path] to start with a forward slash [/] on NFS type" -}} + {{- end -}} + + {{- if not $objectData.server -}} + {{- fail "Persistence - Expected non-empty [server] on NFS type" -}} + {{- end }} +- name: {{ $objectData.shortName }} + nfs: + path: {{ $path }} + server: {{ tpl $objectData.server $rootCtx }} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/workload/_pod.tpl b/library/common/1.0.0/templates/lib/workload/_pod.tpl index 587b4ec0ca..19d7fde48e 100644 --- a/library/common/1.0.0/templates/lib/workload/_pod.tpl +++ b/library/common/1.0.0/templates/lib/workload/_pod.tpl @@ -45,5 +45,8 @@ tolerations: #TODO:securityContext: #TODO:containers: #TODO:initContainers: -#TODO:volumes: +{{- with (include "ix.v1.common.lib.pod.volumes" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim) }} +volumes: + {{- . | nindent 2 }} +{{- end -}} {{- end -}}