From 5b1abdd839325a9444850a59f5e6d1a237329745 Mon Sep 17 00:00:00 2001 From: Stavros Kois <47820033+stavros-k@users.noreply.github.com> Date: Mon, 20 Feb 2023 15:23:33 +0200 Subject: [PATCH] NAS-118930 / 23.10 / Improve/Refactor Common Library (#917) * fix * fix * some more * somefixs * whops * initial structure * finish up configmap * secret class * runtest secret * move files arround * ignore * make clear on call template that need root context * imagePullSecret (minus targetSelector) * move out of the way * clean up comment * deployment basic spec * daemonset basic spec * statefulset spec * split file * docs * update values * job spec * job docs * cronJob basic spec * job in cron test * add common version * podsepc * whoopsis * selectorlabels and pod metadata * job and cron pod metadata * update docs * consistent order * get ready for pod * first targetSelector * remove todo * update docs * add hostnet and enableservicelinks * update selector logic * update docs * add tests for restartpolicy * schedulerName * priorityclassname * hostname * termperiodsec * nodeselector * add fail case * host aliases * dns policy * dns config * tolerations * serviceaccoutn class, spawner, saname selector * add pod todo * update some tests * add runtimeclassname * controllers -> workload and plurar to singular * require at least 1 primary on enabled SAs * fix script * remove wrong comment * update naming scheme * update rbac values ref * rbac docs * rbac's * append short name, for future use * update comments * initial service wireframe * shorten line * simplify labels and update tests * service selectors * simplify error messages * finish clusterIP type * loadbalancer * noedport * externalname * external ip * update service * fix highlighting * session affinity * add comment * update comments * service ports * fix indentation * externalname can have no ports * fixup externalIP * add pvc class and spawner and tests * add nfs and emptyDir vols * example * extend docs a bit * not create pvc if existing claim is set * helm... you are dumb really. how this fixes an unrelated test * add configmap * add secret vol * add pvc vol * add hostpath * finish volumes * initial podsec * podsec context with some todo's to check * automatic sysctls * remove todo * update doc struct * split docs * split service docs * initial container plumbing * fix tests * fix test * rename to class * command and args * termination * add lifecycle * int value from tpl * another case * fix service protocol tpl * update readme * ports * update todo * cleanup values a bit * only add sysctl when port is bellow 1024 * whops, thats a different range * update avlue * move some old docs to the "to be deleted" dir * externalinteface validation * update an error message and apply externalinterface annotations to workloads * external interfaces * TZ - TIMEZONE * update rdoc * reduce code duple * device vol type * initial certificate plumbing * update comments * finish secret creation of certificate * cert dosc * volumeMounts * scale certs * doc * add tests for volMounts * values updates * update todo * add test case * remove some todo * update todos * vct * remove tdoo * restore default * rename function * make selectorlabels a bit better * trim * some cleanup * update some ci values * update ci * rollingup defaults * rename dir * fix nil pointers * check the same strategy var * whops * fix tests * typo * not a good day for copy paste * move check * move another check * fix some tests for upcoming probes * one mroe * split docs * add default probes for `main` and docs * add probes and some ci testruns * whops * fix an edge case * add an error for edge case * runtests * runtest updaets * update * check if podvalues exist first * force types * force only one of the 2 * quote labels and annotaions values * job/cron have auto gen selectors * remove false test * fix maxsureg * fix end * different fix * fix some tests * fix rollUp * try to fix 3.9.4 helm * move file to helpers * use capital types in probes and lifecycle * Revert "use capital types in probes and lifecycle" This reverts commit 380ebd5f1fe84cf8cf5026fd6e6827c2de06d5b6. * typo * use lowercase for protocol everywhere * rbac runtest * prune old * add resources * add resources * fix rbc * fix sa naming in pod * fix test * 44 suppl group on gpu * remove todo * extract function in another file * whops * add securityContext implementation * add fail cases * add rest of the tests * remove todo * envFrom * minify * env list * add env * add envdupe check tests * add fixed envs * replace containers with callers * add callers * add initContainer * add init run test * reset default test val * add name tests * add some more tests * rename * validate workload type only if enabled * lint fix for 3.9.4 * add tpl on init enabled * whops * fix init * echo * echo * args... * list * comment out disabled persistences * fix some typos and improve resources `requests` requirement * improve docs a bit * require name,description,version,type * add some wording regarding what Helm Template column means * add title as requirement * remove scheduler * remove priority class name * remove nfs + externalIP * remove LB * remove STS & VCT * fix a test * remove nodeselector * remove DS * remove pvc * remove todo * conditionally print the type, as we might want to use the template to select all objects inthe chart * add some docs * docs for notes * add `tls.` in the certificate secret, according to k8s docs * add some basic docs around the rest of the options * clean values.yaml * catch an edge case * remove externalName * set autmountSA on SA to false * add note about the automountSA --- .github/ct-install-config/chart_schema.yaml | 39 + .github/ct-install-config/ct-install.yaml | 8 + .github/ct-install-config/ct-lint.yaml | 7 + .github/ct-install-config/lint-conf.yaml | 42 + .github/workflows/common_library_tests.yaml | 196 ++++ .gitignore | 3 + .markdownlint.yaml | 10 + helm_template_common.sh | 33 + library/common-test/Chart.yaml | 17 + library/common-test/ci/basic-values.yaml | 42 + library/common-test/ci/configmap-values.yaml | 77 ++ library/common-test/ci/cron-values.yaml | 44 + .../ci/imagePullSecret-values.yaml | 58 ++ library/common-test/ci/init-values.yaml | 61 ++ library/common-test/ci/job-values.yaml | 43 + .../common-test/ci/persistence-values.yaml | 73 ++ library/common-test/ci/rbac-values.yaml | 114 +++ library/common-test/ci/secrets-values.yaml | 77 ++ library/common-test/default-values.yaml | 15 + library/common-test/templates/NOTES.txt | 1 + library/common-test/templates/common.yaml | 1 + .../tests/certificate/data_test.yaml | 30 + .../tests/certificate/metadata_test.yaml | 59 ++ .../tests/certificate/name_test.yaml | 41 + .../tests/certificate/validation_test.yaml | 143 +++ .../tests/configmap/data_test.yaml | 92 ++ .../tests/configmap/metadata_test.yaml | 56 ++ .../tests/configmap/name_test.yaml | 36 + .../tests/configmap/validation_test.yaml | 69 ++ .../common-test/tests/container/arg_test.yaml | 123 +++ .../tests/container/command_test.yaml | 118 +++ .../tests/container/envFixed_test .yaml | 360 +++++++ .../tests/container/envFrom_test.yaml | 258 ++++++ .../tests/container/envList_test.yaml | 85 ++ .../tests/container/env_dupe_test.yaml | 252 +++++ .../common-test/tests/container/env_test.yaml | 402 ++++++++ .../tests/container/image_test.yaml | 139 +++ .../tests/container/lifecycle_test.yaml | 272 ++++++ .../tests/container/name_test.yaml | 50 + .../tests/container/ports_test.yaml | 284 ++++++ .../tests/container/probes_test.yaml | 673 ++++++++++++++ .../tests/container/resources_test.yaml | 877 ++++++++++++++++++ .../tests/container/securityContext_test.yaml | 778 ++++++++++++++++ .../tests/container/termination_test.yaml | 143 +++ .../tests/container/tty_stdin_test.yaml | 72 ++ .../tests/container/validation_test.yaml | 41 + .../tests/container/volumeMounts_test.yaml | 709 ++++++++++++++ .../tests/cronjob/metadata_test.yaml | 85 ++ .../common-test/tests/cronjob/spec_test.yaml | 83 ++ .../tests/cronjob/validation_test.yaml | 43 + .../tests/deployment/metadata_test.yaml | 148 +++ .../tests/deployment/spec_test.yaml | 96 ++ .../tests/deployment/validation_test.yaml | 30 + .../externalInterface/metadata_test.yaml | 238 +++++ .../tests/externalInterface/name_test.yaml | 43 + .../externalInterface/validation_test.yaml | 137 +++ .../tests/imagePullSecret/data_test.yaml | 48 + .../tests/imagePullSecret/metadata_test.yaml | 59 ++ .../tests/imagePullSecret/name_test.yaml | 42 + .../imagePullSecret/validation_test.yaml | 129 +++ .../tests/initContainer/data_test.yaml | 153 +++ .../initContainer/data_upgrade_test.yaml | 155 ++++ .../tests/initContainer/name_test.yaml | 42 + .../initContainer/name_upgrade_test.yaml | 44 + .../tests/initContainer/validation_test.yaml | 68 ++ .../common-test/tests/job/metadata_test.yaml | 84 ++ library/common-test/tests/job/spec_test.yaml | 54 ++ .../tests/job/validation_test.yaml | 45 + .../tests/persistence/validation_test.yaml | 35 + .../tests/pod/automount_sa_token_test.yaml | 75 ++ .../tests/pod/dns_config_test.yaml | 237 +++++ .../tests/pod/dns_policy_test.yaml | 106 +++ .../tests/pod/enable_service_links_test.yaml | 75 ++ .../tests/pod/host-aliases_test.yaml | 126 +++ .../tests/pod/host_network_test.yaml | 75 ++ .../common-test/tests/pod/hostname_test.yaml | 56 ++ .../tests/pod/image_pull_secret_test.yaml | 155 ++++ .../tests/pod/restart_policy_test.yaml | 137 +++ .../tests/pod/runtime_class_name_test.yaml | 191 ++++ .../tests/pod/securityContext.yaml | 497 ++++++++++ .../tests/pod/service_account_name_test.yaml | 174 ++++ .../pod/termination_grace_period_test.yaml | 72 ++ .../tests/pod/tolerations_test.yaml | 201 ++++ .../tests/pod/volume_configmap_test.yaml | 210 +++++ .../tests/pod/volume_device_test.yaml | 110 +++ .../tests/pod/volume_emptyDIr_test.yaml | 123 +++ .../tests/pod/volume_hostPath_test.yaml | 110 +++ .../tests/pod/volume_ixVolume_test.yaml | 137 +++ .../tests/pod/volume_secret_test.yaml | 211 +++++ library/common-test/tests/rbac/data_test.yaml | 250 +++++ .../common-test/tests/rbac/metadata_test.yaml | 163 ++++ library/common-test/tests/rbac/name_test.yaml | 105 +++ .../tests/rbac/validation_test.yaml | 250 +++++ .../common-test/tests/secret/data_test.yaml | 111 +++ .../tests/secret/metadata_test.yaml | 56 ++ .../common-test/tests/secret/name_test.yaml | 36 + .../tests/secret/validation_test.yaml | 81 ++ .../tests/service/cluster_ip_test.yaml | 126 +++ .../tests/service/metadata_test.yaml | 158 ++++ .../common-test/tests/service/names_test.yaml | 49 + .../tests/service/node_port_test.yaml | 135 +++ .../tests/service/validation_test.yaml | 391 ++++++++ .../tests/serviceAccount/metadata_test.yaml | 59 ++ .../tests/serviceAccount/name_test.yaml | 45 + .../tests/serviceAccount/validation_test.yaml | 77 ++ .../tests/workload/names_test.yaml | 54 ++ .../tests/workload/validation_test.yaml | 120 +++ library/common-test/values.yaml | 33 + library/common/1.0.0/Chart.yaml | 10 + library/common/1.0.0/README.md | 24 + library/common/1.0.0/docs/README.md | 285 ++++++ library/common/1.0.0/docs/configmap.md | 48 + library/common/1.0.0/docs/container/README.md | 93 ++ library/common/1.0.0/docs/container/args.md | 33 + .../common/1.0.0/docs/container/command.md | 35 + library/common/1.0.0/docs/container/env.md | 64 ++ .../common/1.0.0/docs/container/envFrom.md | 46 + .../common/1.0.0/docs/container/envList.md | 42 + .../common/1.0.0/docs/container/fixedEnv.md | 59 ++ .../common/1.0.0/docs/container/lifecycle.md | 49 + library/common/1.0.0/docs/container/probes.md | 80 ++ .../common/1.0.0/docs/container/resources.md | 51 + .../1.0.0/docs/container/securityContext.md | 59 ++ .../1.0.0/docs/container/termination.md | 34 + library/common/1.0.0/docs/imagePullSecret.md | 66 ++ library/common/1.0.0/docs/notes.md | 41 + .../common/1.0.0/docs/persistence/README.md | 97 ++ .../1.0.0/docs/persistence/configmap.md | 35 + .../common/1.0.0/docs/persistence/device.md | 28 + .../common/1.0.0/docs/persistence/emptyDir.md | 25 + .../common/1.0.0/docs/persistence/hostPath.md | 25 + .../common/1.0.0/docs/persistence/ixVolume.md | 25 + .../common/1.0.0/docs/persistence/secret.md | 35 + library/common/1.0.0/docs/rbac.md | 91 ++ library/common/1.0.0/docs/scaleCertificate.md | 85 ++ .../1.0.0/docs/scaleExternalInterface.md | 47 + library/common/1.0.0/docs/scaleGPU.md | 33 + library/common/1.0.0/docs/secret.md | 50 + .../common/1.0.0/docs/service/ClusterIP.md | 50 + library/common/1.0.0/docs/service/NodePort.md | 51 + library/common/1.0.0/docs/service/README.md | 43 + library/common/1.0.0/docs/serviceAccount.md | 60 ++ library/common/1.0.0/docs/workload/README.md | 137 +++ library/common/1.0.0/docs/workload/cronjob.md | 55 ++ .../common/1.0.0/docs/workload/deployment.md | 49 + library/common/1.0.0/docs/workload/job.md | 42 + .../1.0.0/templates/class/_configmap.tpl | 35 + .../common/1.0.0/templates/class/_cronjob.tpl | 51 + .../1.0.0/templates/class/_deployment.tpl | 54 ++ library/common/1.0.0/templates/class/_job.tpl | 51 + .../class/_networkAttachmentDefinition.tpl | 34 + .../common/1.0.0/templates/class/_rbac.tpl | 64 ++ .../common/1.0.0/templates/class/_secret.tpl | 56 ++ .../common/1.0.0/templates/class/_service.tpl | 75 ++ .../1.0.0/templates/class/_serviceAccount.tpl | 33 + .../1.0.0/templates/helpers/_envDupeCheck.tpl | 23 + .../1.0.0/templates/helpers/_getPortRange.tpl | 60 ++ .../templates/helpers/_getSelectedPod.tpl | 47 + .../templates/lib/certificate/_getData.tpl | 40 + .../templates/lib/certificate/_validation.tpl | 18 + .../1.0.0/templates/lib/chart/_names.tpl | 41 + .../1.0.0/templates/lib/chart/_notes.tpl | 21 + .../templates/lib/configmap/_validation.tpl | 21 + .../1.0.0/templates/lib/container/_args.tpl | 22 + .../templates/lib/container/_command.tpl | 18 + .../1.0.0/templates/lib/container/_env.tpl | 84 ++ .../templates/lib/container/_envFrom.tpl | 59 ++ .../templates/lib/container/_envList.tpl | 19 + .../templates/lib/container/_fixedEnv.tpl | 67 ++ .../lib/container/_imageSelector.tpl | 42 + .../templates/lib/container/_lifecycle.tpl | 37 + .../1.0.0/templates/lib/container/_ports.tpl | 80 ++ .../lib/container/_primaryValidation.tpl | 40 + .../1.0.0/templates/lib/container/_probes.tpl | 98 ++ .../templates/lib/container/_resources.tpl | 138 +++ .../lib/container/_securityContext.tpl | 119 +++ .../templates/lib/container/_termination.tpl | 33 + .../templates/lib/container/_volumeMounts.tpl | 95 ++ .../probe-lifecycle-actions/_exec.tpl | 18 + .../probe-lifecycle-actions/_grpc.tpl | 23 + .../probe-lifecycle-actions/_httpGet.tpl | 53 ++ .../probe-lifecycle-actions/_tcpSocket.tpl | 23 + .../lib/externalInterface/_validation.tpl | 53 ++ .../lib/imagePullSecret/_createData.tpl | 42 + .../lib/imagePullSecret/_validation.tpl | 27 + .../lib/metadata/_allAnnotations.tpl | 9 + .../templates/lib/metadata/_allLabels.tpl | 13 + .../_externalInterfaceAnnotations.tpl | 52 ++ .../lib/metadata/_globalAnnotations.tpl | 6 + .../templates/lib/metadata/_globalLabels.tpl | 6 + .../lib/metadata/_podAnnotations.tpl | 7 + .../templates/lib/metadata/_podLabels.tpl | 6 + .../1.0.0/templates/lib/metadata/_render.tpl | 28 + .../lib/metadata/_selectorLabels.tpl | 16 + .../templates/lib/metadata/_validation.tpl | 22 + .../lib/pod/_autoMountServiceAccountToken.tpl | 24 + .../1.0.0/templates/lib/pod/_container.tpl | 62 ++ .../templates/lib/pod/_containerSpawner.tpl | 31 + .../common/1.0.0/templates/lib/pod/_dns.tpl | 90 ++ .../templates/lib/pod/_enableServiceLinks.tpl | 24 + .../1.0.0/templates/lib/pod/_hostAliases.tpl | 37 + .../1.0.0/templates/lib/pod/_hostNetwork.tpl | 24 + .../1.0.0/templates/lib/pod/_hostname.tpl.tpl | 22 + .../templates/lib/pod/_imagePullSecret.tpl | 38 + .../lib/pod/_initContainerSpawner.tpl | 83 ++ .../templates/lib/pod/_podSecurityContext.tpl | 90 ++ .../templates/lib/pod/_restartPolicy.tpl | 34 + .../templates/lib/pod/_runtimeClassName.tpl | 48 + .../templates/lib/pod/_serviceAccountName.tpl | 48 + .../pod/_terminationGracePeriodSeconds.tpl | 29 + .../1.0.0/templates/lib/pod/_tolerations.tpl | 67 ++ .../1.0.0/templates/lib/pod/_volumes.tpl | 62 ++ .../templates/lib/pod/volumes/_configmap.tpl | 57 ++ .../templates/lib/pod/volumes/_device.tpl | 35 + .../templates/lib/pod/volumes/_emptyDir.tpl | 35 + .../templates/lib/pod/volumes/_hostPath.tpl | 35 + .../templates/lib/pod/volumes/_ixVolume.tpl | 48 + .../templates/lib/pod/volumes/_secret.tpl | 57 ++ .../lib/rbac/_getServiceAccounts.tpl | 52 ++ .../1.0.0/templates/lib/rbac/_rules.tpl | 50 + .../1.0.0/templates/lib/rbac/_subjects.tpl | 17 + .../1.0.0/templates/lib/rbac/_validation.tpl | 38 + .../templates/lib/secret/_validation.tpl | 25 + .../1.0.0/templates/lib/service/_ports.tpl | 63 ++ .../templates/lib/service/_validation.tpl | 131 +++ .../service/serviceTypeConfig/_cluster_ip.tpl | 16 + .../serviceTypeConfig/_externalIPs.tpl | 17 + .../_externalTrafficPolicy.tpl | 22 + .../service/serviceTypeConfig/_ipFamily.tpl | 38 + .../_publishNotReadyAddresses.tpl | 19 + .../serviceTypeConfig/_sessionAffinity.tpl | 42 + .../service/serviceTypeSpecs/_clusterIP.tpl | 21 + .../service/serviceTypeSpecs/_nodePort.tpl | 22 + .../lib/serviceAccount/_validation.tpl | 38 + .../templates/lib/storage/_validation.tpl | 22 + .../templates/lib/workload/_cronjobSpec.tpl | 26 + .../lib/workload/_deployementSpec.tpl | 29 + .../1.0.0/templates/lib/workload/_jobSpec.tpl | 24 + .../1.0.0/templates/lib/workload/_pod.tpl | 49 + .../validation/_cronjobValidation.tpl | 29 + .../validation/_deploymentValidation.tpl | 30 + .../workload/validation/_jobValidation.tpl | 32 + .../validation/_workloadValidation.tpl | 43 + .../common/1.0.0/templates/loader/_all.tpl | 8 + .../common/1.0.0/templates/loader/_apply.tpl | 31 + .../common/1.0.0/templates/loader/_init.tpl | 8 + .../1.0.0/templates/spawner/_certificate.tpl | 39 + .../1.0.0/templates/spawner/_configmap.tpl | 32 + .../templates/spawner/_externalInterface.tpl | 32 + .../templates/spawner/_imagePullSecret.tpl | 40 + .../common/1.0.0/templates/spawner/_rbac.tpl | 43 + .../1.0.0/templates/spawner/_secret.tpl | 32 + .../1.0.0/templates/spawner/_service.tpl | 39 + .../templates/spawner/_serviceAccount.tpl | 38 + .../1.0.0/templates/spawner/_workload.tpl | 52 ++ .../common/1.0.0/templates/values/_init.tpl | 14 + library/common/1.0.0/values.yaml | 173 ++++ run_common_tests.sh | 27 + 258 files changed, 21205 insertions(+) create mode 100644 .github/ct-install-config/chart_schema.yaml create mode 100644 .github/ct-install-config/ct-install.yaml create mode 100644 .github/ct-install-config/ct-lint.yaml create mode 100644 .github/ct-install-config/lint-conf.yaml create mode 100644 .github/workflows/common_library_tests.yaml create mode 100644 .markdownlint.yaml create mode 100755 helm_template_common.sh create mode 100644 library/common-test/Chart.yaml create mode 100644 library/common-test/ci/basic-values.yaml create mode 100644 library/common-test/ci/configmap-values.yaml create mode 100644 library/common-test/ci/cron-values.yaml create mode 100644 library/common-test/ci/imagePullSecret-values.yaml create mode 100644 library/common-test/ci/init-values.yaml create mode 100644 library/common-test/ci/job-values.yaml create mode 100644 library/common-test/ci/persistence-values.yaml create mode 100644 library/common-test/ci/rbac-values.yaml create mode 100644 library/common-test/ci/secrets-values.yaml create mode 100644 library/common-test/default-values.yaml create mode 100644 library/common-test/templates/NOTES.txt create mode 100644 library/common-test/templates/common.yaml create mode 100644 library/common-test/tests/certificate/data_test.yaml create mode 100644 library/common-test/tests/certificate/metadata_test.yaml create mode 100644 library/common-test/tests/certificate/name_test.yaml create mode 100644 library/common-test/tests/certificate/validation_test.yaml create mode 100644 library/common-test/tests/configmap/data_test.yaml create mode 100644 library/common-test/tests/configmap/metadata_test.yaml create mode 100644 library/common-test/tests/configmap/name_test.yaml create mode 100644 library/common-test/tests/configmap/validation_test.yaml create mode 100644 library/common-test/tests/container/arg_test.yaml create mode 100644 library/common-test/tests/container/command_test.yaml create mode 100644 library/common-test/tests/container/envFixed_test .yaml create mode 100644 library/common-test/tests/container/envFrom_test.yaml create mode 100644 library/common-test/tests/container/envList_test.yaml create mode 100644 library/common-test/tests/container/env_dupe_test.yaml create mode 100644 library/common-test/tests/container/env_test.yaml create mode 100644 library/common-test/tests/container/image_test.yaml create mode 100644 library/common-test/tests/container/lifecycle_test.yaml create mode 100644 library/common-test/tests/container/name_test.yaml create mode 100644 library/common-test/tests/container/ports_test.yaml create mode 100644 library/common-test/tests/container/probes_test.yaml create mode 100644 library/common-test/tests/container/resources_test.yaml create mode 100644 library/common-test/tests/container/securityContext_test.yaml create mode 100644 library/common-test/tests/container/termination_test.yaml create mode 100644 library/common-test/tests/container/tty_stdin_test.yaml create mode 100644 library/common-test/tests/container/validation_test.yaml create mode 100644 library/common-test/tests/container/volumeMounts_test.yaml create mode 100644 library/common-test/tests/cronjob/metadata_test.yaml create mode 100644 library/common-test/tests/cronjob/spec_test.yaml create mode 100644 library/common-test/tests/cronjob/validation_test.yaml create mode 100644 library/common-test/tests/deployment/metadata_test.yaml create mode 100644 library/common-test/tests/deployment/spec_test.yaml create mode 100644 library/common-test/tests/deployment/validation_test.yaml create mode 100644 library/common-test/tests/externalInterface/metadata_test.yaml create mode 100644 library/common-test/tests/externalInterface/name_test.yaml create mode 100644 library/common-test/tests/externalInterface/validation_test.yaml create mode 100644 library/common-test/tests/imagePullSecret/data_test.yaml create mode 100644 library/common-test/tests/imagePullSecret/metadata_test.yaml create mode 100644 library/common-test/tests/imagePullSecret/name_test.yaml create mode 100644 library/common-test/tests/imagePullSecret/validation_test.yaml create mode 100644 library/common-test/tests/initContainer/data_test.yaml create mode 100644 library/common-test/tests/initContainer/data_upgrade_test.yaml create mode 100644 library/common-test/tests/initContainer/name_test.yaml create mode 100644 library/common-test/tests/initContainer/name_upgrade_test.yaml create mode 100644 library/common-test/tests/initContainer/validation_test.yaml create mode 100644 library/common-test/tests/job/metadata_test.yaml create mode 100644 library/common-test/tests/job/spec_test.yaml create mode 100644 library/common-test/tests/job/validation_test.yaml create mode 100644 library/common-test/tests/persistence/validation_test.yaml create mode 100644 library/common-test/tests/pod/automount_sa_token_test.yaml create mode 100644 library/common-test/tests/pod/dns_config_test.yaml create mode 100644 library/common-test/tests/pod/dns_policy_test.yaml create mode 100644 library/common-test/tests/pod/enable_service_links_test.yaml create mode 100644 library/common-test/tests/pod/host-aliases_test.yaml create mode 100644 library/common-test/tests/pod/host_network_test.yaml create mode 100644 library/common-test/tests/pod/hostname_test.yaml create mode 100644 library/common-test/tests/pod/image_pull_secret_test.yaml create mode 100644 library/common-test/tests/pod/restart_policy_test.yaml create mode 100644 library/common-test/tests/pod/runtime_class_name_test.yaml create mode 100644 library/common-test/tests/pod/securityContext.yaml create mode 100644 library/common-test/tests/pod/service_account_name_test.yaml create mode 100644 library/common-test/tests/pod/termination_grace_period_test.yaml create mode 100644 library/common-test/tests/pod/tolerations_test.yaml create mode 100644 library/common-test/tests/pod/volume_configmap_test.yaml create mode 100644 library/common-test/tests/pod/volume_device_test.yaml create mode 100644 library/common-test/tests/pod/volume_emptyDIr_test.yaml create mode 100644 library/common-test/tests/pod/volume_hostPath_test.yaml create mode 100644 library/common-test/tests/pod/volume_ixVolume_test.yaml create mode 100644 library/common-test/tests/pod/volume_secret_test.yaml create mode 100644 library/common-test/tests/rbac/data_test.yaml create mode 100644 library/common-test/tests/rbac/metadata_test.yaml create mode 100644 library/common-test/tests/rbac/name_test.yaml create mode 100644 library/common-test/tests/rbac/validation_test.yaml create mode 100644 library/common-test/tests/secret/data_test.yaml create mode 100644 library/common-test/tests/secret/metadata_test.yaml create mode 100644 library/common-test/tests/secret/name_test.yaml create mode 100644 library/common-test/tests/secret/validation_test.yaml create mode 100644 library/common-test/tests/service/cluster_ip_test.yaml create mode 100644 library/common-test/tests/service/metadata_test.yaml create mode 100644 library/common-test/tests/service/names_test.yaml create mode 100644 library/common-test/tests/service/node_port_test.yaml create mode 100644 library/common-test/tests/service/validation_test.yaml create mode 100644 library/common-test/tests/serviceAccount/metadata_test.yaml create mode 100644 library/common-test/tests/serviceAccount/name_test.yaml create mode 100644 library/common-test/tests/serviceAccount/validation_test.yaml create mode 100644 library/common-test/tests/workload/names_test.yaml create mode 100644 library/common-test/tests/workload/validation_test.yaml create mode 100644 library/common-test/values.yaml create mode 100644 library/common/1.0.0/Chart.yaml create mode 100644 library/common/1.0.0/README.md create mode 100644 library/common/1.0.0/docs/README.md create mode 100644 library/common/1.0.0/docs/configmap.md create mode 100644 library/common/1.0.0/docs/container/README.md create mode 100644 library/common/1.0.0/docs/container/args.md create mode 100644 library/common/1.0.0/docs/container/command.md create mode 100644 library/common/1.0.0/docs/container/env.md create mode 100644 library/common/1.0.0/docs/container/envFrom.md create mode 100644 library/common/1.0.0/docs/container/envList.md create mode 100644 library/common/1.0.0/docs/container/fixedEnv.md create mode 100644 library/common/1.0.0/docs/container/lifecycle.md create mode 100644 library/common/1.0.0/docs/container/probes.md create mode 100644 library/common/1.0.0/docs/container/resources.md create mode 100644 library/common/1.0.0/docs/container/securityContext.md create mode 100644 library/common/1.0.0/docs/container/termination.md create mode 100644 library/common/1.0.0/docs/imagePullSecret.md create mode 100644 library/common/1.0.0/docs/notes.md create mode 100644 library/common/1.0.0/docs/persistence/README.md create mode 100644 library/common/1.0.0/docs/persistence/configmap.md create mode 100644 library/common/1.0.0/docs/persistence/device.md create mode 100644 library/common/1.0.0/docs/persistence/emptyDir.md create mode 100644 library/common/1.0.0/docs/persistence/hostPath.md create mode 100644 library/common/1.0.0/docs/persistence/ixVolume.md create mode 100644 library/common/1.0.0/docs/persistence/secret.md create mode 100644 library/common/1.0.0/docs/rbac.md create mode 100644 library/common/1.0.0/docs/scaleCertificate.md create mode 100644 library/common/1.0.0/docs/scaleExternalInterface.md create mode 100644 library/common/1.0.0/docs/scaleGPU.md create mode 100644 library/common/1.0.0/docs/secret.md create mode 100644 library/common/1.0.0/docs/service/ClusterIP.md create mode 100644 library/common/1.0.0/docs/service/NodePort.md create mode 100644 library/common/1.0.0/docs/service/README.md create mode 100644 library/common/1.0.0/docs/serviceAccount.md create mode 100644 library/common/1.0.0/docs/workload/README.md create mode 100644 library/common/1.0.0/docs/workload/cronjob.md create mode 100644 library/common/1.0.0/docs/workload/deployment.md create mode 100644 library/common/1.0.0/docs/workload/job.md create mode 100644 library/common/1.0.0/templates/class/_configmap.tpl create mode 100644 library/common/1.0.0/templates/class/_cronjob.tpl create mode 100644 library/common/1.0.0/templates/class/_deployment.tpl create mode 100644 library/common/1.0.0/templates/class/_job.tpl create mode 100644 library/common/1.0.0/templates/class/_networkAttachmentDefinition.tpl create mode 100644 library/common/1.0.0/templates/class/_rbac.tpl create mode 100644 library/common/1.0.0/templates/class/_secret.tpl create mode 100644 library/common/1.0.0/templates/class/_service.tpl create mode 100644 library/common/1.0.0/templates/class/_serviceAccount.tpl create mode 100644 library/common/1.0.0/templates/helpers/_envDupeCheck.tpl create mode 100644 library/common/1.0.0/templates/helpers/_getPortRange.tpl create mode 100644 library/common/1.0.0/templates/helpers/_getSelectedPod.tpl create mode 100644 library/common/1.0.0/templates/lib/certificate/_getData.tpl create mode 100644 library/common/1.0.0/templates/lib/certificate/_validation.tpl create mode 100644 library/common/1.0.0/templates/lib/chart/_names.tpl create mode 100644 library/common/1.0.0/templates/lib/chart/_notes.tpl create mode 100644 library/common/1.0.0/templates/lib/configmap/_validation.tpl create mode 100644 library/common/1.0.0/templates/lib/container/_args.tpl create mode 100644 library/common/1.0.0/templates/lib/container/_command.tpl create mode 100644 library/common/1.0.0/templates/lib/container/_env.tpl create mode 100644 library/common/1.0.0/templates/lib/container/_envFrom.tpl create mode 100644 library/common/1.0.0/templates/lib/container/_envList.tpl create mode 100644 library/common/1.0.0/templates/lib/container/_fixedEnv.tpl create mode 100644 library/common/1.0.0/templates/lib/container/_imageSelector.tpl create mode 100644 library/common/1.0.0/templates/lib/container/_lifecycle.tpl create mode 100644 library/common/1.0.0/templates/lib/container/_ports.tpl create mode 100644 library/common/1.0.0/templates/lib/container/_primaryValidation.tpl create mode 100644 library/common/1.0.0/templates/lib/container/_probes.tpl create mode 100644 library/common/1.0.0/templates/lib/container/_resources.tpl create mode 100644 library/common/1.0.0/templates/lib/container/_securityContext.tpl create mode 100644 library/common/1.0.0/templates/lib/container/_termination.tpl create mode 100644 library/common/1.0.0/templates/lib/container/_volumeMounts.tpl create mode 100644 library/common/1.0.0/templates/lib/container/probe-lifecycle-actions/_exec.tpl create mode 100644 library/common/1.0.0/templates/lib/container/probe-lifecycle-actions/_grpc.tpl create mode 100644 library/common/1.0.0/templates/lib/container/probe-lifecycle-actions/_httpGet.tpl create mode 100644 library/common/1.0.0/templates/lib/container/probe-lifecycle-actions/_tcpSocket.tpl create mode 100644 library/common/1.0.0/templates/lib/externalInterface/_validation.tpl create mode 100644 library/common/1.0.0/templates/lib/imagePullSecret/_createData.tpl create mode 100644 library/common/1.0.0/templates/lib/imagePullSecret/_validation.tpl create mode 100644 library/common/1.0.0/templates/lib/metadata/_allAnnotations.tpl create mode 100644 library/common/1.0.0/templates/lib/metadata/_allLabels.tpl create mode 100644 library/common/1.0.0/templates/lib/metadata/_externalInterfaceAnnotations.tpl create mode 100644 library/common/1.0.0/templates/lib/metadata/_globalAnnotations.tpl create mode 100644 library/common/1.0.0/templates/lib/metadata/_globalLabels.tpl create mode 100644 library/common/1.0.0/templates/lib/metadata/_podAnnotations.tpl create mode 100644 library/common/1.0.0/templates/lib/metadata/_podLabels.tpl create mode 100644 library/common/1.0.0/templates/lib/metadata/_render.tpl create mode 100644 library/common/1.0.0/templates/lib/metadata/_selectorLabels.tpl create mode 100644 library/common/1.0.0/templates/lib/metadata/_validation.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/_autoMountServiceAccountToken.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/_container.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/_containerSpawner.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/_dns.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/_enableServiceLinks.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/_hostAliases.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/_hostNetwork.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/_hostname.tpl.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/_imagePullSecret.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/_initContainerSpawner.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/_podSecurityContext.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/_restartPolicy.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/_runtimeClassName.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/_serviceAccountName.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/_terminationGracePeriodSeconds.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/_tolerations.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/_volumes.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/volumes/_configmap.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/volumes/_device.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/volumes/_emptyDir.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/volumes/_hostPath.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/volumes/_ixVolume.tpl create mode 100644 library/common/1.0.0/templates/lib/pod/volumes/_secret.tpl create mode 100644 library/common/1.0.0/templates/lib/rbac/_getServiceAccounts.tpl create mode 100644 library/common/1.0.0/templates/lib/rbac/_rules.tpl create mode 100644 library/common/1.0.0/templates/lib/rbac/_subjects.tpl create mode 100644 library/common/1.0.0/templates/lib/rbac/_validation.tpl create mode 100644 library/common/1.0.0/templates/lib/secret/_validation.tpl create mode 100644 library/common/1.0.0/templates/lib/service/_ports.tpl create mode 100644 library/common/1.0.0/templates/lib/service/_validation.tpl create mode 100644 library/common/1.0.0/templates/lib/service/serviceTypeConfig/_cluster_ip.tpl create mode 100644 library/common/1.0.0/templates/lib/service/serviceTypeConfig/_externalIPs.tpl create mode 100644 library/common/1.0.0/templates/lib/service/serviceTypeConfig/_externalTrafficPolicy.tpl create mode 100644 library/common/1.0.0/templates/lib/service/serviceTypeConfig/_ipFamily.tpl create mode 100644 library/common/1.0.0/templates/lib/service/serviceTypeConfig/_publishNotReadyAddresses.tpl create mode 100644 library/common/1.0.0/templates/lib/service/serviceTypeConfig/_sessionAffinity.tpl create mode 100644 library/common/1.0.0/templates/lib/service/serviceTypeSpecs/_clusterIP.tpl create mode 100644 library/common/1.0.0/templates/lib/service/serviceTypeSpecs/_nodePort.tpl create mode 100644 library/common/1.0.0/templates/lib/serviceAccount/_validation.tpl create mode 100644 library/common/1.0.0/templates/lib/storage/_validation.tpl create mode 100644 library/common/1.0.0/templates/lib/workload/_cronjobSpec.tpl create mode 100644 library/common/1.0.0/templates/lib/workload/_deployementSpec.tpl create mode 100644 library/common/1.0.0/templates/lib/workload/_jobSpec.tpl create mode 100644 library/common/1.0.0/templates/lib/workload/_pod.tpl create mode 100644 library/common/1.0.0/templates/lib/workload/validation/_cronjobValidation.tpl create mode 100644 library/common/1.0.0/templates/lib/workload/validation/_deploymentValidation.tpl create mode 100644 library/common/1.0.0/templates/lib/workload/validation/_jobValidation.tpl create mode 100644 library/common/1.0.0/templates/lib/workload/validation/_workloadValidation.tpl create mode 100644 library/common/1.0.0/templates/loader/_all.tpl create mode 100644 library/common/1.0.0/templates/loader/_apply.tpl create mode 100644 library/common/1.0.0/templates/loader/_init.tpl create mode 100644 library/common/1.0.0/templates/spawner/_certificate.tpl create mode 100644 library/common/1.0.0/templates/spawner/_configmap.tpl create mode 100644 library/common/1.0.0/templates/spawner/_externalInterface.tpl create mode 100644 library/common/1.0.0/templates/spawner/_imagePullSecret.tpl create mode 100644 library/common/1.0.0/templates/spawner/_rbac.tpl create mode 100644 library/common/1.0.0/templates/spawner/_secret.tpl create mode 100644 library/common/1.0.0/templates/spawner/_service.tpl create mode 100644 library/common/1.0.0/templates/spawner/_serviceAccount.tpl create mode 100644 library/common/1.0.0/templates/spawner/_workload.tpl create mode 100644 library/common/1.0.0/templates/values/_init.tpl create mode 100644 library/common/1.0.0/values.yaml create mode 100755 run_common_tests.sh diff --git a/.github/ct-install-config/chart_schema.yaml b/.github/ct-install-config/chart_schema.yaml new file mode 100644 index 0000000000..59bbdd47b4 --- /dev/null +++ b/.github/ct-install-config/chart_schema.yaml @@ -0,0 +1,39 @@ +name: str(required=True) +title: str(required=True) +description: str(required=True) +version: str(required=True) +type: str(required=True) +kubeVersion: str() +apiVersion: str() +appVersion: any(str(), num()) +upstream_version: any(str(), num(), required=False) +home: str() +icon: str() +sources: list(str(), required=False) +dependencies: list(include('dependency'), required=False) +deprecated: bool(required=False) +engine: str(required=False) +condition: str(required=False) +keywords: list(str(), required=False) +tags: str(required=False) +maintainers: list(include('maintainer')) +annotations: map(str(), str(), required=False) +--- +maintainer: + name: str() + email: str(required=False) + url: str(required=True) +--- +dependency: + name: str() + repository: str() + version: str() + condition: str(required=False) + tags: list(str(), required=False) + import-values: any(list(str()), list(include('import-value')), required=False) + enabled: bool(required=False) + alias: str(required=False) +--- +import-value: + child: str() + parent: str() diff --git a/.github/ct-install-config/ct-install.yaml b/.github/ct-install-config/ct-install.yaml new file mode 100644 index 0000000000..45121f564e --- /dev/null +++ b/.github/ct-install-config/ct-install.yaml @@ -0,0 +1,8 @@ +remote: origin +target-branch: master +helm-extra-args: --timeout 600s --debug +chart-yaml-schema: .github/ct-install-config/chart_schema.yaml +chart-dirs: + - library + - charts +excluded-charts: [] diff --git a/.github/ct-install-config/ct-lint.yaml b/.github/ct-install-config/ct-lint.yaml new file mode 100644 index 0000000000..6dc691ffe1 --- /dev/null +++ b/.github/ct-install-config/ct-lint.yaml @@ -0,0 +1,7 @@ +remote: origin +target-branch: master +helm-extra-args: --timeout 600s --debug +chart-yaml-schema: .github/ct-install-config/chart_schema.yaml +chart-dirs: + - library +excluded-charts: [] diff --git a/.github/ct-install-config/lint-conf.yaml b/.github/ct-install-config/lint-conf.yaml new file mode 100644 index 0000000000..90f48c889b --- /dev/null +++ b/.github/ct-install-config/lint-conf.yaml @@ -0,0 +1,42 @@ +--- +rules: + braces: + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: -1 + brackets: + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: -1 + colons: + max-spaces-before: 0 + max-spaces-after: 1 + commas: + max-spaces-before: 0 + min-spaces-after: 1 + max-spaces-after: 1 + comments: + require-starting-space: true + min-spaces-from-content: 2 + document-end: disable + document-start: disable # No --- to start a file + empty-lines: + max: 2 + max-start: 0 + max-end: 0 + hyphens: + max-spaces-after: 1 + indentation: + spaces: consistent + indent-sequences: whatever # - list indentation will handle both indentation and without + check-multi-line-strings: false + key-duplicates: enable + line-length: disable # Lines can be any length + new-line-at-end-of-file: enable + new-lines: + type: unix + trailing-spaces: enable + truthy: + level: warning diff --git a/.github/workflows/common_library_tests.yaml b/.github/workflows/common_library_tests.yaml new file mode 100644 index 0000000000..fb403e9dee --- /dev/null +++ b/.github/workflows/common_library_tests.yaml @@ -0,0 +1,196 @@ +name: Common Library Tests + +on: + pull_request: + paths: + - library/** + - .github/workflows/common_library_tests.yaml + +jobs: + lint: + name: Lint Common + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + helm-version: + - v3.9.4 + - v3.10.3 + - v3.11.1 + steps: + - name: Checkout + uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3 + with: + fetch-depth: 1 + + - name: Install Helm + uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78 # tag=v3 + with: + version: ${{ matrix.helm-version }} + + - uses: actions/setup-python@2c3dd9e7e29afd70cc0950079bde6c979d1f69f9 # tag=v4 + with: + python-version: "3.10" + + - name: Set up chart-testing + uses: helm/chart-testing-action@afea100a513515fbd68b0e72a7bb0ae34cb62aec # tag=v2.3.1 + + - name: Run chart-testing (lint) + id: lint + run: | + ct lint --config .github/ct-install-config/ct-lint.yaml \ + --lint-conf .github/ct-install-config/lint-conf.yaml \ + --charts library/common-test \ + --debug + + unittest: + needs: + - lint + name: Unit Tests + runs-on: ubuntu-22.04 + env: + helmUnitVersion: 0.2.11 + strategy: + fail-fast: false + matrix: + helm-version: + - v3.9.4 + - v3.10.3 + - v3.11.1 + steps: + - name: Checkout + uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3 + with: + fetch-depth: 1 + + - name: Install Helm + uses: azure/setup-helm@f382f75448129b3be48f8121b9857be18d815a82 # tag=v3 + with: + version: ${{ matrix.helm-version }} + + - name: Run Unit-tests + shell: bash + run: | + # Retry helm plugin install + helm plugin install https://github.com/quintush/helm-unittest --version v${helmUnitVersion} || \ + helm plugin install https://github.com/quintush/helm-unittest --version v${helmUnitVersion} || \ + helm plugin install https://github.com/quintush/helm-unittest --version v${helmUnitVersion} || \ + helm plugin install https://github.com/quintush/helm-unittest --version v${helmUnitVersion} + + # Run tests + cd library/common-test/ + helm dependency update + helm unittest --helm3 -f "tests/*/*.yaml" . + + install: + needs: + - lint + name: Install Charts + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + # We run tests on k3s version of latest SCALE release, SCALE nightly and manually defined "latest" + k3s-version: + - v1.25.3+k3s1 + # We run tests on Helm version of latest SCALE release, SCALE nightly and manually defined "latest" + helm-version: + - v3.11.1 + values: + - basic-values.yaml + - configmap-values.yaml + - secrets-values.yaml + - imagePullSecret-values.yaml + - job-values.yaml + - cron-values.yaml + - persistence-values.yaml + - rbac-values.yaml + - init-values.yaml + + steps: + - name: Checkout + uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3 + with: + fetch-depth: 1 + + - name: Install Helm + uses: azure/setup-helm@f382f75448129b3be48f8121b9857be18d815a82 # tag=v3 + with: + version: ${{ matrix.helm-version }} + + - uses: actions/setup-python@2c3dd9e7e29afd70cc0950079bde6c979d1f69f9 # tag=v4 + with: + python-version: "3.10" + + - name: Set up chart-testing + uses: helm/chart-testing-action@afea100a513515fbd68b0e72a7bb0ae34cb62aec # tag=v2.3.1 + + - name: Create k3d cluster - Attempt 1/3 + continue-on-error: true + id: createc1 + uses: nolar/setup-k3d-k3s@293b8e5822a20bc0d5bcdd4826f1a665e72aba96 # tag=v1.0.9 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + version: ${{ matrix.k3s-version }} + # Flags found here https://github.com/k3d-io/k3d + k3d-args: --k3s-arg --disable=metrics-server@server:* + + - name: Wait 10 second to retry + if: steps.createc1.outcome=='failure' + run: | + sleep 10 + - name: Create k3d cluster - Attempt 2/3 + continue-on-error: true + if: steps.createc1.outcome=='failure' + id: createc2 + uses: nolar/setup-k3d-k3s@293b8e5822a20bc0d5bcdd4826f1a665e72aba96 # tag=v1.0.9 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + version: ${{ matrix.k3s-version }} + # Flags found here https://github.com/k3d-io/k3d + k3d-args: --k3s-arg --disable=metrics-server@server:* + + - name: Wait 10 second to retry + if: steps.createc2.outcome=='failure' + run: | + sleep 10 + - name: Create k3d cluster - Attempt 3/3 + id: createc3 + if: steps.createc2.outcome=='failure' + uses: nolar/setup-k3d-k3s@293b8e5822a20bc0d5bcdd4826f1a665e72aba96 # tag=v1.0.9 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + version: ${{ matrix.k3s-version }} + # Flags found here https://github.com/k3d-io/k3d + k3d-args: --k3s-arg --disable=metrics-server@server:* + + # Install Kail to grab logs from tests, as there are cases ct-install fail to output logs + - name: Install Kail + run: | + export KAIL_VERSION=v0.16.1 + wget https://github.com/boz/kail/releases/download/${KAIL_VERSION}/kail_${KAIL_VERSION}_linux_amd64.tar.gz + tar -xvzf kail_${KAIL_VERSION}_linux_amd64.tar.gz + chmod +x kail + + - name: Run chart-testing (install) + run: | + # Move all ci values on a temp location (or skip if already moved from another matrix job) + mv library/common-test/ci library/common-test/runtests || echo "Nothing to move" + + # Move one values.yaml to the correct location to run the test + mv -f library/common-test/runtests/${{ matrix.values }} library/common-test/values.yaml + + # Stat kail on the background to grab logs from tests + ./kail --ignore-ns kube-system >> /tmp/output.log & + + # Actually run the test + ct install --config .github/ct-install-config/ct-install.yaml \ + --charts library/common-test \ + --debug || (echo -e "\n\n--===PODLOGS===--\n\n" && \ + cat /tmp/output.log && \ + rm -f /tmp/output.log && exit 1) + + kill $! + echo -e "\n\n--===PODLOGS===--\n\n" + cat /tmp/output.log + rm -f /tmp/output.log diff --git a/.gitignore b/.gitignore index 800c4971c4..1c82b266dd 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,6 @@ npm-debug.log tests/artifacts/ tests/test-report.txt tests/test-report.xml +__snapshot__/ +library/common-test/Chart.lock +library/common-test/charts diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 0000000000..655c3eec63 --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,10 @@ +# https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md033 +# Do not warn for "Inline HTML" +MD033: false + +# https://github.com/DavidAnson/markdownlint/blob/v0.26.2/doc/Rules.md#md024 +# Multiple headings with the same content +MD024: false + +MD013: + line_length: 300 diff --git a/helm_template_common.sh b/helm_template_common.sh new file mode 100755 index 0000000000..c988da8dc5 --- /dev/null +++ b/helm_template_common.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +chart_path=library/common-test + +if [ ! $1 == "template" ]; then + if [ $1 == "-f" ] && [ ! -z $2 ]; then + extra_args=("-f" "$chart_path/ci/$2") + fi +fi + +function cleanup { + if [ -d "$chart_path/charts" ]; then + echo "🧹 Cleaning up charts..." + rm -r "$chart_path/charts" + rm "$chart_path/Chart.lock" + fi +} + +cleanup + +echo "Building common..." +helm dependency update "$chart_path" + +if [ $1 == "template" ]; then + echo "🧪 Running =1.16.0-0' +description: A chart for the common library chart-testing +home: http://localhost +type: application +icon: https://localhost/icon +dependencies: + - name: common + repository: file://../common/1.0.0 + version: ~1.0.0 +maintainers: + - name: truenas + url: https://www.truenas.com/ diff --git a/library/common-test/ci/basic-values.yaml b/library/common-test/ci/basic-values.yaml new file mode 100644 index 0000000000..19541bed35 --- /dev/null +++ b/library/common-test/ci/basic-values.yaml @@ -0,0 +1,42 @@ +image: + repository: traefik/whoami + pullPolicy: IfNotPresent + tag: latest + +service: + main: + enabled: true + primary: true + ports: + main: + enabled: true + primary: true + protocol: http + port: 8080 + +workload: + main: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + main: + enabled: true + primary: true + args: + - --port + - "8080" + probes: + liveness: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + readiness: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + startup: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" diff --git a/library/common-test/ci/configmap-values.yaml b/library/common-test/ci/configmap-values.yaml new file mode 100644 index 0000000000..a22fcc7918 --- /dev/null +++ b/library/common-test/ci/configmap-values.yaml @@ -0,0 +1,77 @@ +image: + repository: traefik/whoami + pullPolicy: IfNotPresent + tag: latest + +service: + main: + enabled: true + primary: true + ports: + main: + enabled: true + primary: true + protocol: http + port: 8080 + +workload: + main: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + main: + enabled: true + primary: true + args: + - --port + - "8080" + probes: + liveness: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + readiness: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + startup: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + + +key1: value1 +key2: 80 +key3: pair +key4: value2 +key5: 81 +key6: false + +envFrom: + - configMapRef: + name: kv-test + +configmap: + kv-test: + enabled: true + data: + key: "{{ .Values.key1 }}" + key1: "{{ .Values.key4 }}" + key2: "{{ .Values.key5 }}" + key3: "{{ .Values.key6 }}" + + file-test: + enabled: true + data: + nginx.conf2: | + alias {{ .Values.key3 }} + listen {{ .Values.key2 }} + function { + # some json + "key": { + "key2": "value", + "key3": "value2" + } + } diff --git a/library/common-test/ci/cron-values.yaml b/library/common-test/ci/cron-values.yaml new file mode 100644 index 0000000000..19f80d5048 --- /dev/null +++ b/library/common-test/ci/cron-values.yaml @@ -0,0 +1,44 @@ +image: + repository: traefik/whoami + pullPolicy: IfNotPresent + tag: latest + +service: + main: + enabled: true + primary: true + ports: + main: + enabled: true + primary: true + protocol: http + port: 8080 + +workload: + main: + enabled: true + primary: true + type: CronJob + schedule: "*/1 * * * *" + podSpec: + restartPolicy: OnFailure + containers: + main: + enabled: true + primary: true + args: + - --port + - "8080" + probes: + liveness: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + readiness: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + startup: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" diff --git a/library/common-test/ci/imagePullSecret-values.yaml b/library/common-test/ci/imagePullSecret-values.yaml new file mode 100644 index 0000000000..b2a21c04f4 --- /dev/null +++ b/library/common-test/ci/imagePullSecret-values.yaml @@ -0,0 +1,58 @@ +image: + repository: traefik/whoami + pullPolicy: IfNotPresent + tag: latest + +service: + main: + enabled: true + primary: true + ports: + main: + enabled: true + primary: true + protocol: http + port: 8080 + +workload: + main: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + main: + enabled: true + primary: true + args: + - --port + - "8080" + probes: + liveness: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + readiness: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + startup: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + +registry: quay.io +user: user +pass: secret_pass +email: mail@example.com + +imagePullSecret: + image-secret-name: + enabled: true + labels: {} + annotations: {} + data: + registry: "{{ .Values.registry }}" + username: "{{ .Values.user }}" + password: "{{ .Values.pass }}" + email: "{{ .Values.email }}" diff --git a/library/common-test/ci/init-values.yaml b/library/common-test/ci/init-values.yaml new file mode 100644 index 0000000000..065af7cf0c --- /dev/null +++ b/library/common-test/ci/init-values.yaml @@ -0,0 +1,61 @@ +image: + repository: traefik/whoami + pullPolicy: IfNotPresent + tag: latest +initImage: + repository: bash + pullPolicy: IfNotPresent + tag: latest + +workload: + main: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + main: + enabled: true + primary: true + args: + - --port + - "8080" + probes: + liveness: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + readiness: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + startup: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + initContainers: + init-cont: + enabled: true + type: init + imageSelector: initImage + args: + - echo + - "Hello World" + install-cont: + enabled: true + type: install + imageSelector: initImage + args: + - echo + - "Hello World" + +service: + main: + enabled: true + primary: true + ports: + main: + enabled: true + primary: true + protocol: http + port: 8080 diff --git a/library/common-test/ci/job-values.yaml b/library/common-test/ci/job-values.yaml new file mode 100644 index 0000000000..4e56a52bd9 --- /dev/null +++ b/library/common-test/ci/job-values.yaml @@ -0,0 +1,43 @@ +image: + repository: traefik/whoami + pullPolicy: IfNotPresent + tag: latest + +service: + main: + enabled: true + primary: true + ports: + main: + enabled: true + primary: true + protocol: http + port: 8080 + +workload: + main: + enabled: true + primary: true + type: Job + podSpec: + restartPolicy: Never + containers: + main: + enabled: true + primary: true + args: + - --port + - "8080" + probes: + liveness: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + readiness: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + startup: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" diff --git a/library/common-test/ci/persistence-values.yaml b/library/common-test/ci/persistence-values.yaml new file mode 100644 index 0000000000..0cc468d1ea --- /dev/null +++ b/library/common-test/ci/persistence-values.yaml @@ -0,0 +1,73 @@ +image: + repository: tccr.io/truecharts/whoami + pullPolicy: IfNotPresent + tag: 1.8.7@sha256:8c61f0ca92fd806fcb4ed1465cb793c05443f37951554b105b0f2dc686a95772 + +service: + main: + enabled: true + primary: true + ports: + main: + enabled: true + primary: true + port: 8080 + +workload: + main: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + main: + enabled: true + primary: true + args: + - --port + - "8080" + probes: + liveness: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + readiness: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + startup: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + +persistence: + emptydir: + enabled: true + type: emptyDir + mountPath: /emptydir + + emptydir-mem: + enabled: true + type: emptyDir + mountPath: /emptydirmem + medium: Memory + + emptydir-size: + enabled: true + type: emptyDir + mountPath: /emptydirsize + size: 1Gi + + emptydir-memsize: + enabled: true + type: emptyDir + mountPath: /emptydirmemsize + medium: Memory + size: 1Gi + + hostpath-stock: + enabled: true + type: hostPath + hostPath: /usr + mountPath: /hptest + hostPathType: "" diff --git a/library/common-test/ci/rbac-values.yaml b/library/common-test/ci/rbac-values.yaml new file mode 100644 index 0000000000..6a8a5bd67b --- /dev/null +++ b/library/common-test/ci/rbac-values.yaml @@ -0,0 +1,114 @@ +image: + repository: traefik/whoami + pullPolicy: IfNotPresent + tag: latest + +service: + main: + enabled: true + primary: true + ports: + main: + enabled: true + primary: true + protocol: http + port: 8080 + +workload: + main: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + main: + enabled: true + primary: true + args: + - --port + - "8080" + probes: + liveness: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + readiness: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + startup: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + workload2: + enabled: true + primary: false + type: Deployment + podSpec: + containers: + main: + enabled: true + primary: true + args: + - --port + - "8080" + probes: + liveness: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + readiness: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + startup: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + +rbac: + main: + enabled: true + primary: true + clusterWide: true + allServiceAccounts: true + labels: + key: value + key1: value1 + annotations: + key: value + key1: value1 + rules: + - apiGroups: + - "" + resources: + - services + - endpoints + - secrets + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch + +serviceAccount: + sa-name: + enabled: true + primary: true + labels: + key: value + key2: value + annotations: + key: value + key2: value + other-sa-name: + enabled: true + targetSelector: + - workload2 diff --git a/library/common-test/ci/secrets-values.yaml b/library/common-test/ci/secrets-values.yaml new file mode 100644 index 0000000000..0c79722072 --- /dev/null +++ b/library/common-test/ci/secrets-values.yaml @@ -0,0 +1,77 @@ +image: + repository: traefik/whoami + pullPolicy: IfNotPresent + tag: latest + +service: + main: + enabled: true + primary: true + ports: + main: + enabled: true + primary: true + protocol: http + port: 8080 + +workload: + main: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + main: + enabled: true + primary: true + args: + - --port + - "8080" + probes: + liveness: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + readiness: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + startup: + enabled: true + type: http + port: "{{ .Values.service.main.ports.main.port }}" + + +key1: value1 +key2: 80 +key3: pair +key4: value2 +key5: 81 +key6: false + +envFrom: + - secretRef: + name: kv-test + +secret: + kv-test: + enabled: true + data: + key: "{{ .Values.key1 }}" + key1: "{{ .Values.key4 }}" + key2: "{{ .Values.key5 }}" + key3: "{{ .Values.key6 }}" + + file-test: + enabled: true + data: + nginx.conf2: | + alias {{ .Values.key3 }} + listen {{ .Values.key2 }} + function { + # some json + "key": { + "key2": "value", + "key3": "value2" + } + } diff --git a/library/common-test/default-values.yaml b/library/common-test/default-values.yaml new file mode 100644 index 0000000000..65ef08337a --- /dev/null +++ b/library/common-test/default-values.yaml @@ -0,0 +1,15 @@ +workload: + main: + enabled: true + podSpec: + containers: + main: + enabled: true + +service: + main: + enabled: true + ports: + main: + enabled: true + port: 80 diff --git a/library/common-test/templates/NOTES.txt b/library/common-test/templates/NOTES.txt new file mode 100644 index 0000000000..af271fbccf --- /dev/null +++ b/library/common-test/templates/NOTES.txt @@ -0,0 +1 @@ +{{- include "ix.v1.common.lib.chart.notes" $ -}} diff --git a/library/common-test/templates/common.yaml b/library/common-test/templates/common.yaml new file mode 100644 index 0000000000..cb793047c6 --- /dev/null +++ b/library/common-test/templates/common.yaml @@ -0,0 +1 @@ +{{- include "ix.v1.common.loader.all" . -}} diff --git a/library/common-test/tests/certificate/data_test.yaml b/library/common-test/tests/certificate/data_test.yaml new file mode 100644 index 0000000000..fe13d5007e --- /dev/null +++ b/library/common-test/tests/certificate/data_test.yaml @@ -0,0 +1,30 @@ +suite: certificate data test +templates: + - common.yaml +chart: + appVersion: &appVer v9.9.9 +tests: + - it: should pass with secret created for certificate + set: + ixCertificates: + "1": + certificate: some_cert + privatekey: some_key + scaleCertificate: + my-cert: + enabled: true + id: 1 + asserts: + - documentIndex: &secretDoc 0 + isKind: + of: Secret + - documentIndex: *secretDoc + equal: + path: data + value: + tls.crt: c29tZV9jZXJ0 + tls.key: c29tZV9rZXk= + - documentIndex: *secretDoc + equal: + path: type + value: kubernetes.io/tls diff --git a/library/common-test/tests/certificate/metadata_test.yaml b/library/common-test/tests/certificate/metadata_test.yaml new file mode 100644 index 0000000000..c32961e90e --- /dev/null +++ b/library/common-test/tests/certificate/metadata_test.yaml @@ -0,0 +1,59 @@ +suite: certificate metadata test +templates: + - common.yaml +chart: + appVersion: &appVer v9.9.9 +tests: + - it: should pass with certificate created with labels and annotations + set: + label1: label1 + label2: global_label2 + annotation1: annotation1 + annotation2: global_annotation2 + global: + labels: + g_label1: global_label1 + g_label2: "{{ .Values.label2 }}" + annotations: + g_annotation1: global_annotation1 + g_annotation2: "{{ .Values.annotation2 }}" + ixCertificates: + "1": + certificate: some_cert + privatekey: some_key + scaleCertificate: + my-cert: + enabled: true + id: 1 + labels: + label1: "{{ .Values.label1 }}" + label2: label2 + annotations: + annotation1: "{{ .Values.annotation1 }}" + annotation2: annotation2 + asserts: + - documentIndex: &secretDoc 0 + isKind: + of: Secret + - documentIndex: *secretDoc + equal: + path: metadata.annotations + value: + annotation1: annotation1 + annotation2: annotation2 + g_annotation1: global_annotation1 + g_annotation2: global_annotation2 + - documentIndex: *secretDoc + equal: + path: metadata.labels + value: + app: common-test-1.0.0 + release: RELEASE-NAME + helm-revision: "0" + helm.sh/chart: common-test-1.0.0 + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: *appVer + g_label1: global_label1 + g_label2: global_label2 + label1: label1 + label2: label2 diff --git a/library/common-test/tests/certificate/name_test.yaml b/library/common-test/tests/certificate/name_test.yaml new file mode 100644 index 0000000000..10ef9f9592 --- /dev/null +++ b/library/common-test/tests/certificate/name_test.yaml @@ -0,0 +1,41 @@ +suite: certificate name test +templates: + - common.yaml +tests: + - it: should generate correct name + set: + ixCertificates: + "1": + certificate: some_cert + privatekey: some_key + "2": + certificate: some_cert + privatekey: some_key + scaleCertificate: + my-cert1: + enabled: true + id: 1 + my-cert2: + enabled: true + id: 2 + asserts: + - documentIndex: &secretDoc 0 + isKind: + of: Secret + - documentIndex: *secretDoc + isAPIVersion: + of: v1 + - documentIndex: *secretDoc + equal: + path: metadata.name + value: release-name-common-test-my-cert1 + - documentIndex: &otherSecretDoc 1 + isKind: + of: Secret + - documentIndex: *otherSecretDoc + isAPIVersion: + of: v1 + - documentIndex: *otherSecretDoc + equal: + path: metadata.name + value: release-name-common-test-my-cert2 diff --git a/library/common-test/tests/certificate/validation_test.yaml b/library/common-test/tests/certificate/validation_test.yaml new file mode 100644 index 0000000000..4fec3eec04 --- /dev/null +++ b/library/common-test/tests/certificate/validation_test.yaml @@ -0,0 +1,143 @@ +suite: certificate validation test +templates: + - common.yaml +tests: + - it: should fail with name longer than 63 characters + set: + scaleCertificate: + my-certificate-super-long-name-that-is-longer-than-63-characters: + enabled: true + id: 1 + asserts: + - failedTemplate: + errorMessage: Name [release-name-common-test-my-certificate-super-long-name-that-is-longer-than-63-characters] is not valid. Must start and end with an alphanumeric lowercase character. It can contain '-'. And must be at most 63 characters. + + - it: should fail with name starting with underscore + set: + scaleCertificate: + _my-cert: + enabled: true + id: 1 + asserts: + - failedTemplate: + errorMessage: Name [release-name-common-test-_my-cert] is not valid. Must start and end with an alphanumeric lowercase character. It can contain '-'. And must be at most 63 characters. + + - it: should fail with labels not a dict + set: + scaleCertificate: + my-cert: + enabled: true + labels: "not a dict" + id: 1 + asserts: + - failedTemplate: + errorMessage: Certificate - Expected to be a dictionary, but got [string] + + - it: should fail with annotations not a dict + set: + scaleCertificate: + my-cert: + enabled: true + annotations: "not a dict" + id: 1 + asserts: + - failedTemplate: + errorMessage: Certificate - Expected to be a dictionary, but got [string] + + - it: should fail without id + set: + scaleCertificate: + my-cert: + enabled: true + id: "" + asserts: + - failedTemplate: + errorMessage: Certificate - Expected non-empty + + - it: should fail with targetSelector not a dict + set: + scaleCertificate: + my-cert: + enabled: true + id: 1 + targetSelector: "not a dict" + asserts: + - failedTemplate: + errorMessage: Certificate - Expected to be a [map], but got [string] + + - it: should fail with empty ixCertificates when cert is defined + set: + ixCertificates: [] + scaleCertificate: + my-cert: + enabled: true + id: 1 + asserts: + - failedTemplate: + errorMessage: Certificate - Expected non-empty + + - it: should fail with not defined id in ixCertificates when cert is defined + set: + ixCertificates: + "2": + key: value + scaleCertificate: + my-cert: + enabled: true + id: 1 + asserts: + - failedTemplate: + errorMessage: Certificate - Expected certificate with ["1"] to exist in + + - it: should fail with with revoked cert + set: + ixCertificates: + "1": + revoked: true + scaleCertificate: + my-cert: + enabled: true + id: 1 + asserts: + - failedTemplate: + errorMessage: Certificate - Expected non-revoked certificate with ["1"] + + - it: should fail with with expired cert + set: + ixCertificates: + "1": + expired: true + scaleCertificate: + my-cert: + enabled: true + id: 1 + asserts: + - failedTemplate: + errorMessage: Certificate - Expected non-expired certificate with ["1"] + + - it: should fail with with empty certificate + set: + ixCertificates: + "1": + certificate: "" + scaleCertificate: + my-cert: + enabled: true + id: 1 + asserts: + - failedTemplate: + errorMessage: Certificate - Expected non-empty [certificate] in certificate with ["1"] in + + - it: should fail with with empty privatekey + set: + ixCertificates: + "1": + certificate: some_value + privatekey: "" + scaleCertificate: + my-cert: + enabled: true + id: 1 + asserts: + - failedTemplate: + errorMessage: Certificate - Expected non-empty [privatekey] in certificate with ["1"] in diff --git a/library/common-test/tests/configmap/data_test.yaml b/library/common-test/tests/configmap/data_test.yaml new file mode 100644 index 0000000000..6ff35063e7 --- /dev/null +++ b/library/common-test/tests/configmap/data_test.yaml @@ -0,0 +1,92 @@ +suite: configmap data test +templates: + - common.yaml +tests: + - it: should pass with key-value data + set: + configmap: + my-configmap1: + enabled: true + data: + foo: bar + asserts: + - documentIndex: &configmapDoc 0 + isKind: + of: ConfigMap + - documentIndex: *configmapDoc + equal: + path: data + value: + foo: bar + + - it: should pass with key-value data from tpl + set: + data: bar + configmap: + my-configmap1: + enabled: true + data: + foo: "{{ .Values.data }}" + asserts: + - documentIndex: *configmapDoc + equal: + path: data + value: + foo: bar + + - it: should pass with scalar data + set: + configmap: + my-configmap1: + enabled: true + data: + foo: | + some multi line + string text + asserts: + - documentIndex: *configmapDoc + equal: + path: data + value: + foo: | + some multi line + string text + + - it: should pass with scalar data with tpl + set: + data: Some other text + configmap: + my-configmap: + enabled: true + data: + foo: | + file start + {{ .Values.data }} + asserts: + - documentIndex: *configmapDoc + equal: + path: data + value: + foo: | + file start + Some other text + + - it: should pass with scalar data from tpl + set: + data: | + Some other text + some_text + configmap: + my-configmap1: + enabled: true + data: + foo: | + {{- .Values.data | nindent 2 }} + asserts: + - documentIndex: *configmapDoc + equal: + path: data + value: + foo: | + Some other text + some_text diff --git a/library/common-test/tests/configmap/metadata_test.yaml b/library/common-test/tests/configmap/metadata_test.yaml new file mode 100644 index 0000000000..39959f8226 --- /dev/null +++ b/library/common-test/tests/configmap/metadata_test.yaml @@ -0,0 +1,56 @@ +suite: configmap metadata test +templates: + - common.yaml +chart: + appVersion: &appVer v9.9.9 +tests: + - it: should pass with configmap created with labels and annotations + set: + label1: label1 + label2: global_label2 + annotation1: annotation1 + annotation2: global_annotation2 + global: + labels: + g_label1: global_label1 + g_label2: "{{ .Values.label2 }}" + annotations: + g_annotation1: global_annotation1 + g_annotation2: "{{ .Values.annotation2 }}" + configmap: + my-configmap1: + enabled: true + labels: + label1: "{{ .Values.label1 }}" + label2: label2 + annotations: + annotation1: "{{ .Values.annotation1 }}" + annotation2: annotation2 + data: + foo: bar + asserts: + - documentIndex: &configMapDoc 0 + isKind: + of: ConfigMap + - documentIndex: *configMapDoc + equal: + path: metadata.annotations + value: + annotation1: annotation1 + annotation2: annotation2 + g_annotation1: global_annotation1 + g_annotation2: global_annotation2 + - documentIndex: *configMapDoc + equal: + path: metadata.labels + value: + app: common-test-1.0.0 + release: RELEASE-NAME + helm-revision: "0" + helm.sh/chart: common-test-1.0.0 + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: *appVer + g_label1: global_label1 + g_label2: global_label2 + label1: label1 + label2: label2 diff --git a/library/common-test/tests/configmap/name_test.yaml b/library/common-test/tests/configmap/name_test.yaml new file mode 100644 index 0000000000..7419b24cae --- /dev/null +++ b/library/common-test/tests/configmap/name_test.yaml @@ -0,0 +1,36 @@ +suite: configmap name test +templates: + - common.yaml +tests: + - it: should generate correct name + set: + configmap: + my-configmap1: + enabled: true + data: + foo: bar + my-configmap2: + enabled: true + data: + foo: bar + asserts: + - documentIndex: &configmapDoc 0 + isKind: + of: ConfigMap + - documentIndex: *configmapDoc + isAPIVersion: + of: v1 + - documentIndex: *configmapDoc + equal: + path: metadata.name + value: release-name-common-test-my-configmap1 + - documentIndex: &otherConfigmapDoc 1 + isKind: + of: ConfigMap + - documentIndex: *otherConfigmapDoc + isAPIVersion: + of: v1 + - documentIndex: *otherConfigmapDoc + equal: + path: metadata.name + value: release-name-common-test-my-configmap2 diff --git a/library/common-test/tests/configmap/validation_test.yaml b/library/common-test/tests/configmap/validation_test.yaml new file mode 100644 index 0000000000..f99e5fd412 --- /dev/null +++ b/library/common-test/tests/configmap/validation_test.yaml @@ -0,0 +1,69 @@ +suite: configmap validation test +templates: + - common.yaml +tests: + - it: should fail with name longer than 63 characters + set: + configmap: + my-configmap-super-long-name-that-is-longer-than-63-characters: + enabled: true + data: + foo: bar + asserts: + - failedTemplate: + errorMessage: Name [release-name-common-test-my-configmap-super-long-name-that-is-longer-than-63-characters] is not valid. Must start and end with an alphanumeric lowercase character. It can contain '-'. And must be at most 63 characters. + + - it: should fail with name starting with underscore + set: + configmap: + _my-configmap: + enabled: true + data: + foo: bar + asserts: + - failedTemplate: + errorMessage: Name [release-name-common-test-_my-configmap] is not valid. Must start and end with an alphanumeric lowercase character. It can contain '-'. And must be at most 63 characters. + + - it: should fail with labels not a dict + set: + configmap: + my-configmap: + enabled: true + labels: "not a dict" + data: + foo: bar + asserts: + - failedTemplate: + errorMessage: ConfigMap - Expected to be a dictionary, but got [string] + + - it: should fail with annotations not a dict + set: + configmap: + my-configmap: + enabled: true + annotations: "not a dict" + data: + foo: bar + asserts: + - failedTemplate: + errorMessage: ConfigMap - Expected to be a dictionary, but got [string] + + - it: should fail with data not a dict + set: + configmap: + my-configmap: + enabled: true + data: "not a dict" + asserts: + - failedTemplate: + errorMessage: ConfigMap - Expected to be a dictionary, but got [string] + + - it: should fail with empty data + set: + configmap: + my-configmap: + enabled: true + data: {} + asserts: + - failedTemplate: + errorMessage: ConfigMap - Expected non-empty diff --git a/library/common-test/tests/container/arg_test.yaml b/library/common-test/tests/container/arg_test.yaml new file mode 100644 index 0000000000..5dfa004632 --- /dev/null +++ b/library/common-test/tests/container/arg_test.yaml @@ -0,0 +1,123 @@ +suite: container arg test +templates: + - common.yaml +tests: + - it: should create the correct multiple args + set: + some_port: 80 + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: &probes + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + args: + - --port + - "{{ .Values.some_port }}" + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + args: + - --port + - "80" + + - it: should create the correct arg command + set: + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + args: --accept-eula + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + args: + - --accept-eula + + - it: should create the correct multiple args and extraArgs + set: + some_path: /some/path + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + args: + - --port + - "80" + extraArgs: + - --accept-eula + - --path + - "{{ .Values.some_path }}" + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + args: + - --port + - "80" + - --accept-eula + - --path + - /some/path diff --git a/library/common-test/tests/container/command_test.yaml b/library/common-test/tests/container/command_test.yaml new file mode 100644 index 0000000000..8e18db0085 --- /dev/null +++ b/library/common-test/tests/container/command_test.yaml @@ -0,0 +1,118 @@ +suite: container command test +templates: + - common.yaml +tests: + - it: should create the correct command in scalar + set: + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: &probes + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + command: + - /bin/sh + - -c + - | + echo "Using image {{ .Values.image.repository }}" + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + command: + - /bin/sh + - -c + - | + echo "Using image nginx" + + - it: should create the correct single command + set: + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + command: ./start.sh + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + command: + - ./start.sh + + - it: should create the correct multiple command + set: + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + command: + - ./print.sh + - "{{ .Values.image.repository }}" + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + command: + - ./print.sh + - nginx diff --git a/library/common-test/tests/container/envFixed_test .yaml b/library/common-test/tests/container/envFixed_test .yaml new file mode 100644 index 0000000000..438a095c56 --- /dev/null +++ b/library/common-test/tests/container/envFixed_test .yaml @@ -0,0 +1,360 @@ +suite: container envFixed test +templates: + - common.yaml +tests: + - it: should create the correct fixed envs + set: + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + TZ: Europe/London + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: &probes + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + env: + - name: TZ + value: Europe/London + - name: UMASK + value: "002" + - name: UMASK_SET + value: "002" + - name: S6_READ_ONLY_ROOT + value: "1" + + - it: should create the correct fixed envs when running as root + set: + image: *image + TZ: Europe/London + securityContext: + container: + runAsUser: 0 + runAsGroup: 0 + runAsNonRoot: false + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + env: + - name: TZ + value: Europe/London + - name: UMASK + value: "002" + - name: UMASK_SET + value: "002" + - name: PUID + value: "568" + - name: USER_ID + value: "568" + - name: UID + value: "568" + - name: PGID + value: "568" + - name: GROUP_ID + value: "568" + - name: GID + value: "568" + - name: S6_READ_ONLY_ROOT + value: "1" + + - it: should create the correct fixed envs when running as root and changed fsGroup + set: + image: *image + TZ: Europe/London + securityContext: + container: + runAsUser: 0 + runAsGroup: 0 + runAsNonRoot: false + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + securityContext: + fsGroup: 1000 + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + env: + - name: TZ + value: Europe/London + - name: UMASK + value: "002" + - name: UMASK_SET + value: "002" + - name: PUID + value: "568" + - name: USER_ID + value: "568" + - name: UID + value: "568" + - name: PGID + value: "1000" + - name: GROUP_ID + value: "1000" + - name: GID + value: "1000" + - name: S6_READ_ONLY_ROOT + value: "1" + + - it: should create the correct fixed envs when running as root and not readonly + set: + image: *image + TZ: Europe/London + securityContext: + container: + runAsUser: 0 + runAsGroup: 0 + runAsNonRoot: false + readOnlyRootFilesystem: false + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + env: + - name: TZ + value: Europe/London + - name: UMASK + value: "002" + - name: UMASK_SET + value: "002" + - name: PUID + value: "568" + - name: USER_ID + value: "568" + - name: UID + value: "568" + - name: PGID + value: "568" + - name: GROUP_ID + value: "568" + - name: GID + value: "568" + + - it: should create the correct fixed envs with GPU + set: + scaleGPU: + - gpu: + nvidia.com/gpu: 1 + targetSelector: + workload-name: + - container-name1 + image: *image + TZ: Europe/London + containerOptions: + NVIDIA_CAPS: + - compute + - video + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + env: + - name: TZ + value: Europe/London + - name: UMASK + value: "002" + - name: UMASK_SET + value: "002" + - name: NVIDIA_DRIVER_CAPABILITIES + value: "compute,video" + - name: S6_READ_ONLY_ROOT + value: "1" + + - it: should create the correct fixed envs with GPU and overrided on container level + set: + scaleGPU: + - gpu: + nvidia.com/gpu: 1 + targetSelector: + workload-name: + - container-name1 + image: *image + TZ: Europe/London + containerOptions: + NVIDIA_CAPS: + - compute + - video + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + fixedEnv: + NVIDIA_CAPS: + - all + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + env: + - name: TZ + value: Europe/London + - name: UMASK + value: "002" + - name: UMASK_SET + value: "002" + - name: NVIDIA_DRIVER_CAPABILITIES + value: "all" + - name: S6_READ_ONLY_ROOT + value: "1" + + # Failures + - it: it should fail with NVIDIA_CAPS having invalid values + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + fixedEnv: + NVIDIA_CAPS: + - invalid + - compute + asserts: + - failedTemplate: + errorMessage: Container - Expected entry to be one of [all, compute, utility, graphics, video], but got [invalid] + + - it: it should fail with NVIDIA_CAPS not having unique values + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + fixedEnv: + NVIDIA_CAPS: + - compute + - compute + asserts: + - failedTemplate: + errorMessage: Container - Expected to have only unique values, but got [compute, compute] diff --git a/library/common-test/tests/container/envFrom_test.yaml b/library/common-test/tests/container/envFrom_test.yaml new file mode 100644 index 0000000000..06b74b7a7c --- /dev/null +++ b/library/common-test/tests/container/envFrom_test.yaml @@ -0,0 +1,258 @@ +suite: container envFrom test +templates: + - common.yaml +tests: + - it: should create the correct envFrom + set: + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + configmap: + configmap-name: + enabled: true + data: + key1: value1 + key2: value2 + secret: + secret-name: + enabled: true + data: + key3: value3 + key4: value4 + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: &probes + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + envFrom: + - configMapRef: + name: configmap-name + - secretRef: + name: secret-name + asserts: + - documentIndex: &deploymentDoc 2 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + envFrom: + - configMapRef: + name: release-name-common-test-configmap-name + - secretRef: + name: release-name-common-test-secret-name + + - it: should create the correct envFrom without expanding the name + set: + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + some_config_name: configmap-name + some_secret_name: secret-name + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: &probes + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + envFrom: + - configMapRef: + name: "{{ .Values.some_config_name }}" + expandObjectName: false + - secretRef: + name: "{{ .Values.some_secret_name }}" + expandObjectName: false + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + envFrom: + - configMapRef: + name: configmap-name + - secretRef: + name: secret-name + + # Failures + - it: it should fail without a valid ref + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + envFrom: + - invalidRef: + name: invalid-name + asserts: + - failedTemplate: + errorMessage: Container - Expected entry to have one of [configMapRef, secretRef] + + - it: it should fail with both refs in the same entry + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + envFrom: + - configMapRef: + name: configmap-name + secretRef: + name: secret-name + asserts: + - failedTemplate: + errorMessage: Container - Expected entry to have only one of [configMapRef, secretRef], but got both + + - it: it should fail with empty name in configMapRef + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + envFrom: + - configMapRef: + name: "" + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty + + - it: it should fail with empty name in secretRef + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + envFrom: + - secretRef: + name: "" + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty + + - it: it should fail without defined configmap + set: + image: *image + configmap: + configmap-name: + enabled: true + data: + key1: value1 + key2: value2 + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + envFrom: + - configMapRef: + name: configmap-other-name + asserts: + - failedTemplate: + errorMessage: Container - Expected ConfigMap [configmap-other-name] defined in to exist + + - it: it should fail without defined secret + set: + image: *image + secret: + secret-name: + enabled: true + data: + key1: value1 + key2: value2 + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + envFrom: + - secretRef: + name: secret-other-name + asserts: + - failedTemplate: + errorMessage: Container - Expected Secret [secret-other-name] defined in to exist diff --git a/library/common-test/tests/container/envList_test.yaml b/library/common-test/tests/container/envList_test.yaml new file mode 100644 index 0000000000..b102ee196d --- /dev/null +++ b/library/common-test/tests/container/envList_test.yaml @@ -0,0 +1,85 @@ +suite: container envList test +templates: + - common.yaml +tests: + - it: should create the correct envList + set: + some_value: value1 + some_other_value: 2 + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: &probes + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + envList: + - name: env1 + value: "{{ .Values.some_value }}" + - name: env2 + value: "{{ .Values.some_other_value }}" + - name: env3 + value: "" + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[0].env + content: + name: env1 + value: value1 + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[0].env + content: + name: env2 + value: "2" + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[0].env + content: + name: env3 + value: "" + + # Failures + - it: it should fail with empty name + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + envList: + - name: "" + value: some_value + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty diff --git a/library/common-test/tests/container/env_dupe_test.yaml b/library/common-test/tests/container/env_dupe_test.yaml new file mode 100644 index 0000000000..693a30592d --- /dev/null +++ b/library/common-test/tests/container/env_dupe_test.yaml @@ -0,0 +1,252 @@ +suite: container env dupe test +templates: + - common.yaml +tests: +# Failures + - it: should fail with dupe env in env and envList + set: + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: &probes + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + env: + VAR1: some_value + envList: + - name: VAR1 + value: 123 + asserts: + - failedTemplate: + errorMessage: Container - Environment Variable [VAR1] in [envList] tried to override the Environment Variable that is already defined in [env] + + - it: should fail with dupe env in env and envFrom configmap + set: + image: *image + configmap: + configmap-name: + enabled: true + data: + VAR1: value + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + env: + VAR1: some_value + envFrom: + - configMapRef: + name: configmap-name + asserts: + - failedTemplate: + errorMessage: Container - Environment Variable [VAR1] in [env] tried to override the Environment Variable that is already defined in [ConfigMap - configmap-name] + + - it: should fail with dupe env in env and envFrom secret + set: + image: *image + secret: + secret-name: + enabled: true + data: + VAR1: value + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + env: + VAR1: some_value + envFrom: + - secretRef: + name: secret-name + asserts: + - failedTemplate: + errorMessage: Container - Environment Variable [VAR1] in [env] tried to override the Environment Variable that is already defined in [Secret - secret-name] + + - it: should fail with dupe env in envList and envFrom secret + set: + image: *image + secret: + secret-name: + enabled: true + data: + VAR1: value + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + envList: + - name: VAR1 + value: some_value + envFrom: + - secretRef: + name: secret-name + asserts: + - failedTemplate: + errorMessage: Container - Environment Variable [VAR1] in [envList] tried to override the Environment Variable that is already defined in [Secret - secret-name] + + - it: should fail with dupe env in envList and envFrom configmap + set: + image: *image + configmap: + configmap-name: + enabled: true + data: + VAR1: value + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + envList: + - name: VAR1 + value: some_value + envFrom: + - configMapRef: + name: configmap-name + asserts: + - failedTemplate: + errorMessage: Container - Environment Variable [VAR1] in [envList] tried to override the Environment Variable that is already defined in [ConfigMap - configmap-name] + + - it: should fail with dupe env in fixedEnv and env + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + env: + TZ: some_value + asserts: + - failedTemplate: + errorMessage: Container - Environment Variable [TZ] in [env] tried to override the Environment Variable that is already defined in [fixedEnv] + + - it: should fail with dupe env in fixedEnv and envList + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + envList: + - name: TZ + value: some_value + asserts: + - failedTemplate: + errorMessage: Container - Environment Variable [TZ] in [envList] tried to override the Environment Variable that is already defined in [fixedEnv] + + - it: should fail with dupe env in fixedEnv and envFrom configmap + set: + image: *image + configmap: + configmap-name: + enabled: true + data: + TZ: value + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + envFrom: + - configMapRef: + name: configmap-name + asserts: + - failedTemplate: + errorMessage: Container - Environment Variable [TZ] in [fixedEnv] tried to override the Environment Variable that is already defined in [ConfigMap - configmap-name] + + - it: should fail with dupe env in fixedEnv and envFrom secret + set: + image: *image + secret: + secret-name: + enabled: true + data: + TZ: value + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + envFrom: + - secretRef: + name: secret-name + asserts: + - failedTemplate: + errorMessage: Container - Environment Variable [TZ] in [fixedEnv] tried to override the Environment Variable that is already defined in [Secret - secret-name] diff --git a/library/common-test/tests/container/env_test.yaml b/library/common-test/tests/container/env_test.yaml new file mode 100644 index 0000000000..a0b6b9eb56 --- /dev/null +++ b/library/common-test/tests/container/env_test.yaml @@ -0,0 +1,402 @@ +suite: container env test +templates: + - common.yaml +tests: + - it: should create the correct env + set: + some_value: value1 + some_other_value: 2 + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + configmap: + configmap-name: + enabled: true + data: + key1: value1 + key2: value2 + secret: + secret-name: + enabled: true + data: + key1: value1 + key2: value2 + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: &probes + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + env: + VAR1: "{{ .Values.some_value }}" + VAR2: some_value + VAR3: + configMapKeyRef: + name: configmap-name + key: key1 + VAR4: + secretKeyRef: + name: secret-name + key: key2 + VAR5: + configMapKeyRef: + expandObjectName: false + name: configmap-name + key: key3 + VAR6: + secretKeyRef: + expandObjectName: false + name: secret-name + key: key4 + VAR7: + fieldRef: + fieldPath: metadata.name + asserts: + - documentIndex: &deploymentDoc 2 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[0].env + content: + name: VAR1 + value: value1 + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[0].env + content: + name: VAR2 + value: some_value + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[0].env + content: + name: VAR3 + valueFrom: + configMapKeyRef: + key: key1 + name: release-name-common-test-configmap-name + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[0].env + content: + name: VAR4 + valueFrom: + secretKeyRef: + key: key2 + name: release-name-common-test-secret-name + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[0].env + content: + name: VAR5 + valueFrom: + configMapKeyRef: + key: key3 + name: configmap-name + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[0].env + content: + name: VAR6 + valueFrom: + secretKeyRef: + key: key4 + name: secret-name + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[0].env + content: + name: VAR7 + valueFrom: + fieldRef: + fieldPath: metadata.name + + # Failures + - it: it should fail invalid ref + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + env: + VAR: + invalidKeyRef: {} + asserts: + - failedTemplate: + errorMessage: Container - Expected with a ref to have one of [configMapKeyRef, secretKeyRef, fieldRef], but got [invalidKeyRef] + + - it: it should fail with more than one ref + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + env: + VAR: + configMapKeyRef: {} + secretKeyRef: {} + asserts: + - failedTemplate: + errorMessage: Container - Expected with a ref to have one of [configMapKeyRef, secretKeyRef, fieldRef], but got [configMapKeyRef, secretKeyRef] + + - it: it should fail with empty name in configMapKeyRef + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + env: + VAR: + configMapKeyRef: + name: "" + key: key + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty + + - it: it should fail with empty key in configMapKeyRef + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + env: + VAR: + configMapKeyRef: + name: name + key: "" + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty + + - it: it should fail with empty name in secretKeyRef + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + env: + VAR: + secretKeyRef: + name: "" + key: key + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty + + - it: it should fail with empty key in secretKeyRef + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + env: + VAR: + secretKeyRef: + name: name + key: "" + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty + + - it: it should fail with referenced secret not defined + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + env: + VAR: + secretKeyRef: + name: secret-name + key: somekey + asserts: + - failedTemplate: + errorMessage: Container - Expected in the referenced Secret [secret-name] to be defined + + - it: it should fail with referenced configmap not defined + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + env: + VAR: + configMapKeyRef: + name: configmap-name + key: somekey + asserts: + - failedTemplate: + errorMessage: Container - Expected in the referenced Configmap [configmap-name] to be defined + + - it: it should fail with referenced key in configmap not defined + set: + image: *image + configmap: + configmap-name: + enabled: true + data: + key1: value1 + key2: value2 + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + env: + VAR: + configMapKeyRef: + name: configmap-name + key: somekey + asserts: + - failedTemplate: + errorMessage: Container - Expected in the referenced key [somekey] in Configmap [configmap-name] to be defined + + - it: it should fail with referenced key in secret not defined + set: + image: *image + secret: + secret-name: + enabled: true + data: + key1: value1 + key2: value2 + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + env: + VAR: + secretKeyRef: + name: secret-name + key: somekey + asserts: + - failedTemplate: + errorMessage: Container - Expected in the referenced key [somekey] in Secret [secret-name] to be defined + + - it: it should fail with empty fieldPath in fieldRef + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + env: + VAR: + fieldRef: + fieldPath: "" + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty diff --git a/library/common-test/tests/container/image_test.yaml b/library/common-test/tests/container/image_test.yaml new file mode 100644 index 0000000000..248b013751 --- /dev/null +++ b/library/common-test/tests/container/image_test.yaml @@ -0,0 +1,139 @@ +suite: container image test +templates: + - common.yaml +tests: + - it: should generate correct image + set: + imageDictToUse: image + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + imageGPU: + repository: some-gpu-image + tag: 5.20.0 + pullPolicy: Always + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: "{{ .Values.imageDictToUse }}" + probes: &probes + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + container-name2: + enabled: true + primary: false + imageSelector: imageGPU + probes: *probes + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + image: nginx:1.19.0 + imagePullPolicy: IfNotPresent + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[1] + content: + image: some-gpu-image:5.20.0 + imagePullPolicy: Always + + # Failures + - it: should fail with imageSelector trying to access non-existent image + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image-dict-that-does-not-exist + asserts: + - failedTemplate: + errorMessage: Container - Expected <.Values.image-dict-that-does-not-exist> to exist + + - it: should fail with empty repository in selected image + set: + image: + repository: "" + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty <.Values.image.repository> + + - it: should fail with empty tag in selected image + set: + image: + repository: nginx + tag: "" + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty <.Values.image.tag> + + - it: should fail with invalid pullPolicy selected image + set: + image: + repository: nginx + tag: 1.19.0 + pullPolicy: invalid + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + asserts: + - failedTemplate: + errorMessage: Container - Expected <.Values.image.pullPolicy> to be one of [IfNotPresent, Always, Never], but got [invalid] diff --git a/library/common-test/tests/container/lifecycle_test.yaml b/library/common-test/tests/container/lifecycle_test.yaml new file mode 100644 index 0000000000..d08c38a5e7 --- /dev/null +++ b/library/common-test/tests/container/lifecycle_test.yaml @@ -0,0 +1,272 @@ +suite: container lifecycle test +templates: + - common.yaml +tests: + - it: should pass with lifecycle + set: + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: &probes + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + lifecycle: + postStart: + type: exec + command: + - echo + - hello + preStop: + type: http + port: 80 + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + lifecycle: + postStart: + exec: + command: + - echo + - hello + preStop: + httpGet: + port: 80 + path: / + scheme: HTTP + + - it: should pass with lifecycle + set: + image: *image + some_command: ./some_command + some_port: 80 + some_host: some_host + some_path: /api/v1 + some_value: 123 + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + lifecycle: + postStart: + type: https + port: "{{ .Values.some_port }}" + host: "{{ .Values.some_host }}" + path: "{{ .Values.some_path }}" + httpHeaders: + key: "{{ .Values.some_value }}" + preStop: + type: exec + command: "{{ .Values.some_command }}" + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + lifecycle: + postStart: + httpGet: + port: 80 + path: /api/v1 + scheme: HTTPS + host: some_host + httpHeaders: + - name: key + value: "123" + preStop: + exec: + command: + - ./some_command + + # Failures + - it: should fail with invalid lifecycle hook + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + probes: *probes + imageSelector: image + lifecycle: + invalid: {} + asserts: + - failedTemplate: + errorMessage: Container - Expected to be one of [preStop, postStart], but got [invalid] + + - it: should fail with empty lifecycle type + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + lifecycle: + preStop: + command: [] + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty + + - it: should fail with invalid lifecycle type + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + lifecycle: + preStop: + type: invalid + command: [] + asserts: + - failedTemplate: + errorMessage: Container - Expected to be one of [exec, http, https], but got [invalid] + + - it: should fail with empty command on exec lifecycle type + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + lifecycle: + preStop: + type: exec + command: [] + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty on [exec] type + + - it: should fail with empty port on http lifecycle type + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + lifecycle: + preStop: + type: http + port: "" + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty on [http] type + + - it: should fail with path not starting with / on http lifecycle type + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + lifecycle: + preStop: + type: http + port: 80 + path: "api/v1" + asserts: + - failedTemplate: + errorMessage: Container - Expected to start with a forward slash [/] on type + + - it: should fail with empty value on httpHeaders on http lifecycle type + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + lifecycle: + preStop: + type: http + port: 80 + httpHeaders: + key: "" + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty on diff --git a/library/common-test/tests/container/name_test.yaml b/library/common-test/tests/container/name_test.yaml new file mode 100644 index 0000000000..c4fc6d845d --- /dev/null +++ b/library/common-test/tests/container/name_test.yaml @@ -0,0 +1,50 @@ +suite: container name test +templates: + - common.yaml +tests: + - it: should generate correct container name + set: + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: &probes + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + container-name2: + enabled: true + primary: false + imageSelector: image + probes: *probes + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + name: release-name-common-test + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[1] + content: + name: release-name-common-test-container-name2 diff --git a/library/common-test/tests/container/ports_test.yaml b/library/common-test/tests/container/ports_test.yaml new file mode 100644 index 0000000000..6523d5b222 --- /dev/null +++ b/library/common-test/tests/container/ports_test.yaml @@ -0,0 +1,284 @@ +suite: container ports test +templates: + - common.yaml +tests: + - it: should create the correct ports without selector + set: + some_port: 80 + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: &probes + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + workload-name2: + enabled: true + primary: false + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + service: + my-service1: + enabled: true + primary: true + ports: + port-name: + enabled: true + primary: true + port: "{{ .Values.some_port }}" + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + ports: + - name: port-name + containerPort: 80 + protocol: TCP + - documentIndex: &otherDeploymentDoc 1 + isKind: + of: Deployment + - documentIndex: *otherDeploymentDoc + isNull: + path: spec.template.spec.containers[0].ports + + - it: should create the correct ports with selector + set: + some_port: 53 + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + workload-name2: + enabled: true + primary: false + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + service: + my-service1: + enabled: true + primary: true + targetSelector: workload-name1 + ports: + port-name: + enabled: true + primary: true + targetSelector: container-name1 + port: 1234 + my-service2: + enabled: true + primary: false + targetSelector: workload-name2 + ports: + port-name: + enabled: true + primary: true + targetSelector: container-name1 + port: 54 + targetPort: "{{ .Values.some_port }}" + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + ports: + - name: port-name + containerPort: 1234 + protocol: TCP + - documentIndex: &otherDeploymentDoc 1 + isKind: + of: Deployment + - documentIndex: *otherDeploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + ports: + - name: port-name + containerPort: 53 + protocol: TCP + + - it: should create the correct ports with hostPort + set: + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + service: + my-service1: + enabled: true + primary: true + ports: + port-name: + enabled: true + primary: true + port: 1234 + targetPort: 5678 + hostPort: 20000 + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + ports: + - name: port-name + containerPort: 5678 + protocol: TCP + hostPort: 20000 + + - it: should create the correct protocol from tpl + set: + some_protocol: https + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + service: + my-service1: + enabled: true + primary: true + ports: + port-name: + enabled: true + primary: true + port: 1234 + protocol: "{{ .Values.some_protocol }}" + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + ports: + - name: port-name + containerPort: 1234 + protocol: TCP + + - it: should create the correct protocol + set: + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + service: + my-service1: + enabled: true + primary: true + ports: + port-name: + enabled: true + primary: true + port: 1234 + protocol: udp + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + ports: + - name: port-name + containerPort: 1234 + protocol: UDP diff --git a/library/common-test/tests/container/probes_test.yaml b/library/common-test/tests/container/probes_test.yaml new file mode 100644 index 0000000000..5826560b31 --- /dev/null +++ b/library/common-test/tests/container/probes_test.yaml @@ -0,0 +1,673 @@ +suite: container probe test +templates: + - common.yaml +tests: + - it: should create the probes correctly + set: + some_port: 80 + some_path: /healthz + some_command: echo + probe_type: exec + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: &probes + liveness: + enabled: true + type: http + port: "{{ .Values.some_port }}" + path: "{{ .Values.some_path }}" + readiness: + enabled: true + type: tcp + port: 123 + startup: + enabled: true + type: "{{ .Values.probe_type }}" + command: + - "{{ .Values.some_command }}" + - hello + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + livenessProbe: + httpGet: + path: /healthz + port: 80 + scheme: HTTP + failureThreshold: 5 + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + readinessProbe: + tcpSocket: + port: 123 + failureThreshold: 5 + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 2 + timeoutSeconds: 5 + startupProbe: + exec: + command: + - echo + - hello + failureThreshold: 60 + initialDelaySeconds: 10 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 2 + + - it: should create https probe + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: &probes + liveness: + enabled: true + type: https + port: 443 + spec: + initialDelaySeconds: 120 + timeoutSeconds: 10 + periodSeconds: 15 + successThreshold: 1 + failureThreshold: 3 + readiness: + enabled: true + type: tcp + port: 443 + spec: + initialDelaySeconds: 50 + timeoutSeconds: 30 + periodSeconds: 8 + successThreshold: 5 + failureThreshold: 9 + startup: + enabled: true + type: tcp + port: 443 + spec: + initialDelaySeconds: 25 + timeoutSeconds: 40 + periodSeconds: 7 + successThreshold: 1 + failureThreshold: 12 + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + livenessProbe: + httpGet: + path: / + port: 443 + scheme: HTTPS + failureThreshold: 3 + initialDelaySeconds: 120 + periodSeconds: 15 + successThreshold: 1 + timeoutSeconds: 10 + readinessProbe: + tcpSocket: + port: 443 + failureThreshold: 9 + initialDelaySeconds: 50 + periodSeconds: 8 + successThreshold: 5 + timeoutSeconds: 30 + startupProbe: + tcpSocket: + port: 443 + failureThreshold: 12 + initialDelaySeconds: 25 + periodSeconds: 7 + successThreshold: 1 + timeoutSeconds: 40 + + # Failures + - it: should fail with invalid probe + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + invalid: + enabled: false + asserts: + - failedTemplate: + errorMessage: Container - Expected probe to be one of [liveness, readiness, startup], but got [invalid] + + - it: should fail without probes defined + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: {} + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty + + - it: should fail without liveness defined + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + readiness: + enabled: false + startup: + enabled: false + asserts: + - failedTemplate: + errorMessage: Container - Expected to be defined + + - it: should fail without readiness defined + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + liveness: + enabled: false + startup: + enabled: false + asserts: + - failedTemplate: + errorMessage: Container - Expected to be defined + + - it: should fail without startup defined + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + liveness: + enabled: false + readiness: + enabled: false + asserts: + - failedTemplate: + errorMessage: Container - Expected to be defined + + - it: should fail with invalid type + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + liveness: + enabled: true + type: invalid + readiness: + enabled: false + startup: + enabled: false + asserts: + - failedTemplate: + errorMessage: Container - Expected probe type to be one of [http, https, tcp, grpc, exec], but got [invalid] + + - it: should fail with successThreshold more than 1 on liveness + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + liveness: + enabled: true + type: http + port: 8080 + spec: + successThreshold: 2 + readiness: + enabled: false + startup: + enabled: false + asserts: + - failedTemplate: + errorMessage: Container - Expected to be 1 on [liveness] probe + + - it: should fail with successThreshold more than 1 on startup + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + startup: + enabled: true + type: http + port: 8080 + spec: + successThreshold: 2 + readiness: + enabled: false + liveness: + enabled: false + asserts: + - failedTemplate: + errorMessage: Container - Expected to be 1 on [startup] probe + + - it: should fail with initialDelaySeconds not a number + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + liveness: + enabled: true + type: http + port: 8080 + spec: + initialDelaySeconds: invalid + readiness: + enabled: false + startup: + enabled: false + asserts: + - failedTemplate: + errorMessage: Container - Expected to be a number, but got [invalid] + + - it: should fail with failureThreshold not a number + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + liveness: + enabled: true + type: http + port: 8080 + spec: + failureThreshold: invalid + readiness: + enabled: false + startup: + enabled: false + asserts: + - failedTemplate: + errorMessage: Container - Expected to be a number, but got [invalid] + + - it: should fail with successThreshold not a number + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + liveness: + enabled: true + type: http + port: 8080 + spec: + successThreshold: invalid + readiness: + enabled: false + startup: + enabled: false + asserts: + - failedTemplate: + errorMessage: Container - Expected to be a number, but got [invalid] + + - it: should fail with timeoutSeconds not a number + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + liveness: + enabled: true + type: http + port: 8080 + spec: + timeoutSeconds: invalid + readiness: + enabled: false + startup: + enabled: false + asserts: + - failedTemplate: + errorMessage: Container - Expected to be a number, but got [invalid] + + - it: should fail with periodSeconds not a number + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + liveness: + enabled: true + type: http + port: 8080 + spec: + periodSeconds: invalid + readiness: + enabled: false + startup: + enabled: false + asserts: + - failedTemplate: + errorMessage: Container - Expected to be a number, but got [invalid] + + - it: should fail with empty command on exec type + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + liveness: + enabled: true + type: exec + command: [] + readiness: + enabled: false + startup: + enabled: false + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty on [exec] type + + - it: should fail with empty port on grpc type + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + liveness: + enabled: true + type: grpc + port: "" + readiness: + enabled: false + startup: + enabled: false + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty on [grpc] type + + - it: should fail with empty port on tcp type + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + liveness: + enabled: true + type: tcp + port: "" + readiness: + enabled: false + startup: + enabled: false + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty on [tcp] type + + - it: should fail with empty port on http type + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + liveness: + enabled: true + type: http + port: "" + readiness: + enabled: false + startup: + enabled: false + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty on [http] type + + - it: should fail with invalid path on http type + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + liveness: + enabled: true + type: http + port: 8080 + path: not-starting-with-slash + readiness: + enabled: false + startup: + enabled: false + asserts: + - failedTemplate: + errorMessage: Container - Expected to start with a forward slash [/] on type + + - it: should fail with empty value in http headers + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + liveness: + enabled: true + type: http + port: not-starting-with-slash + httpHeaders: + key: + readiness: + enabled: false + startup: + enabled: false + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty on diff --git a/library/common-test/tests/container/resources_test.yaml b/library/common-test/tests/container/resources_test.yaml new file mode 100644 index 0000000000..a4fc2104d0 --- /dev/null +++ b/library/common-test/tests/container/resources_test.yaml @@ -0,0 +1,877 @@ +suite: container resources test +templates: + - common.yaml +tests: + - it: should create the resources correctly + set: + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: &probes + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + resources: + limits: + cpu: 4000m + memory: 8Gi + requests: + cpu: 10m + memory: 50Mi + + - it: should override the default limits + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + resources: + limits: + cpu: 2000m + memory: 4Gi + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + resources: + limits: + cpu: 2000m + memory: 4Gi + requests: + cpu: 10m + memory: 50Mi + + - it: should override the default limits.cpu + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + resources: + limits: + cpu: 2000m + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + resources: + limits: + cpu: 2000m + memory: 8Gi + requests: + cpu: 10m + memory: 50Mi + + - it: should override the default limits.memory + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + resources: + limits: + memory: 4Gi + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + resources: + limits: + cpu: 4000m + memory: 4Gi + requests: + cpu: 10m + memory: 50Mi + + - it: should not render limits.cpu + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + resources: + limits: + cpu: 0 + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + resources: + limits: + memory: 8Gi + requests: + cpu: 10m + memory: 50Mi + + - it: should not render limits.memory + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + resources: + limits: + memory: 0 + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + resources: + limits: + cpu: 4000m + requests: + cpu: 10m + memory: 50Mi + + - it: should override the default requests + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + resources: + requests: + cpu: 200m + memory: 1Gi + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + resources: + limits: + cpu: 4000m + memory: 8Gi + requests: + cpu: 200m + memory: 1Gi + + - it: should override the default requests.cpu + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + resources: + requests: + cpu: 200m + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + resources: + limits: + cpu: 4000m + memory: 8Gi + requests: + cpu: 200m + memory: 50Mi + + - it: should override the default requests.memory + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + resources: + requests: + memory: 1Gi + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + resources: + limits: + cpu: 4000m + memory: 8Gi + requests: + cpu: 10m + memory: 1Gi + + - it: should assign GPU on the primary pod/container + set: + image: *image + scaleGPU: + - gpu: + nvidia.com/gpu: 1 + global: + ixChartContext: + addNvidiaRuntimeClass: true + nvidiaRuntimeClassName: nvidia + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + container-name2: + enabled: true + primary: false + imageSelector: image + probes: *probes + workload-name2: + enabled: true + primary: false + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.runtimeClassName + value: nvidia + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + resources: + limits: + cpu: 4000m + memory: 8Gi + nvidia.com/gpu: "1" + requests: + cpu: 10m + memory: 50Mi + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[1] + content: + resources: + limits: + cpu: 4000m + memory: 8Gi + requests: + cpu: 10m + memory: 50Mi + - documentIndex: &otherDeploymentDoc 1 + isKind: + of: Deployment + - documentIndex: *otherDeploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *otherDeploymentDoc + isNull: + path: spec.template.spec.runtimeClassName + - documentIndex: *otherDeploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + resources: + limits: + cpu: 4000m + memory: 8Gi + requests: + cpu: 10m + memory: 50Mi + + - it: should assign GPU on the selected pod/container + set: + image: *image + global: + ixChartContext: + addNvidiaRuntimeClass: true + nvidiaRuntimeClassName: nvidia + scaleGPU: + - gpu: + nvidia.com/gpu: 1 + targetSelector: + workload-name2: + - container-name1 + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + container-name2: + enabled: true + primary: false + imageSelector: image + probes: *probes + workload-name2: + enabled: true + primary: false + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.runtimeClassName + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + resources: + limits: + cpu: 4000m + memory: 8Gi + requests: + cpu: 10m + memory: 50Mi + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[1] + content: + resources: + limits: + cpu: 4000m + memory: 8Gi + requests: + cpu: 10m + memory: 50Mi + - documentIndex: &otherDeploymentDoc 1 + isKind: + of: Deployment + - documentIndex: *otherDeploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *otherDeploymentDoc + equal: + path: spec.template.spec.runtimeClassName + value: nvidia + - documentIndex: *otherDeploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + resources: + limits: + cpu: 4000m + memory: 8Gi + nvidia.com/gpu: "1" + requests: + cpu: 10m + memory: 50Mi + + - it: should assign GPU on the selected pods/containers + set: + image: *image + global: + ixChartContext: + addNvidiaRuntimeClass: true + nvidiaRuntimeClassName: nvidia + scaleGPU: + - gpu: + nvidia.com/gpu: 1 + targetSelector: + workload-name1: + - container-name1 + - container-name2 + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + container-name2: + enabled: true + primary: false + imageSelector: image + probes: *probes + workload-name2: + enabled: true + primary: false + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.runtimeClassName + value: nvidia + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + resources: + limits: + cpu: 4000m + memory: 8Gi + nvidia.com/gpu: "1" + requests: + cpu: 10m + memory: 50Mi + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[1] + content: + resources: + limits: + cpu: 4000m + memory: 8Gi + nvidia.com/gpu: "1" + requests: + cpu: 10m + memory: 50Mi + - documentIndex: &otherDeploymentDoc 1 + isKind: + of: Deployment + - documentIndex: *otherDeploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *otherDeploymentDoc + isNull: + path: spec.template.spec.runtimeClassName + - documentIndex: *otherDeploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + resources: + limits: + cpu: 4000m + memory: 8Gi + requests: + cpu: 10m + memory: 50Mi + + # Failures + - it: should fail with empty requests + set: + image: *image + containerOptions: + resources: + requests: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty + + - it: should fail with empty requests.cpu + set: + image: *image + containerOptions: + resources: + requests: + cpu: "" + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty + + - it: should fail with empty requests.memory + set: + image: *image + containerOptions: + resources: + requests: + cpu: 10m + memory: "" + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty + + - it: should fail with invalid format in requests.cpu + set: + image: *image + containerOptions: + resources: + requests: + cpu: 10MB + memory: 50Mi + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - failedTemplate: + errorMessage: Container - Expected to have one of the following formats [(Plain Integer - eg. 1), (Float - eg. 0.5), (Milicpu - eg. 500m)], but got [10MB] + + - it: should fail with invalid format in limits.cpu + set: + image: *image + containerOptions: + resources: + requests: + cpu: 10m + memory: 50Mi + limits: + cpu: 10MB + memory: 8Gi + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - failedTemplate: + errorMessage: Container - Expected to have one of the following formats [(Plain Integer - eg. 1), (Float - eg. 0.5), (Milicpu - eg. 500m)], but got [10MB] + + - it: should fail with invalid format in requests.memory + set: + image: *image + containerOptions: + resources: + requests: + cpu: 10m + memory: 50MB + limits: + cpu: 4000m + memory: 8Gi + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - failedTemplate: + errorMessage: Container - Expected to have one of the following formats [(Suffixed with E/P/T/G/M/K - eg. 1G), (Suffixed with Ei/Pi/Ti/Gi/Mi/Ki - eg. 1Gi), (Plain Integer in bytes - eg. 1024), (Exponent - eg. 134e6)], but got [50MB] + + - it: should fail with invalid format in limits.memory + set: + image: *image + containerOptions: + resources: + requests: + cpu: 10m + memory: 50Mi + limits: + cpu: 4000m + memory: 8GB + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - failedTemplate: + errorMessage: Container - Expected to have one of the following formats [(Suffixed with E/P/T/G/M/K - eg. 1G), (Suffixed with Ei/Pi/Ti/Gi/Mi/Ki - eg. 1Gi), (Plain Integer in bytes - eg. 1024), (Exponent - eg. 134e6)], but got [8GB] + + - it: should fail with empty gpu in defined entry + set: + image: *image + scaleGPU: + - gpu: + targetSelector: + workload-name1: + - container-name1 + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty + + - it: should fail with empty list under workload in targetSelector + set: + image: *image + scaleGPU: + - gpu: + key: value + targetSelector: + workload-name1: [] + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty list under pod in + + - it: should fail with empty value in gpu + set: + image: *image + scaleGPU: + - gpu: + key: "" + targetSelector: + workload-name1: + - container-name1 + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty diff --git a/library/common-test/tests/container/securityContext_test.yaml b/library/common-test/tests/container/securityContext_test.yaml new file mode 100644 index 0000000000..1b10152be1 --- /dev/null +++ b/library/common-test/tests/container/securityContext_test.yaml @@ -0,0 +1,778 @@ +suite: container security context test +templates: + - common.yaml +tests: + - it: should create the securityContext correctly + set: + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: &probes + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + securityContext: + runAsUser: 568 + runAsGroup: 568 + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + privileged: false + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + capabilities: + add: [] + drop: + - ALL + + - it: should override the securityContext runAsUser and runAsNonRoot + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + securityContext: + runAsUser: 0 + runAsNonRoot: false + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + securityContext: + runAsUser: 0 + runAsGroup: 568 + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + privileged: false + runAsNonRoot: false + seccompProfile: + type: RuntimeDefault + capabilities: + add: [] + drop: + - ALL + + - it: should override the securityContext runAsGroup and runAsNonRoot + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + securityContext: + runAsGroup: 0 + runAsNonRoot: false + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + securityContext: + runAsUser: 568 + runAsGroup: 0 + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + privileged: false + runAsNonRoot: false + seccompProfile: + type: RuntimeDefault + capabilities: + add: [] + drop: + - ALL + + - it: should override the securityContext readOnlyRootFilesystem + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + securityContext: + readOnlyRootFilesystem: false + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + securityContext: + runAsUser: 568 + runAsGroup: 568 + readOnlyRootFilesystem: false + allowPrivilegeEscalation: false + privileged: false + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + capabilities: + add: [] + drop: + - ALL + + - it: should override the securityContext privileged + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + securityContext: + privileged: true + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + securityContext: + runAsUser: 568 + runAsGroup: 568 + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + privileged: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + capabilities: + add: [] + drop: + - ALL + + - it: should override the securityContext allowPrivilegeEscalation + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + securityContext: + allowPrivilegeEscalation: true + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + securityContext: + runAsUser: 568 + runAsGroup: 568 + readOnlyRootFilesystem: true + allowPrivilegeEscalation: true + privileged: false + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + capabilities: + add: [] + drop: + - ALL + + - it: should override the securityContext capabilities.add + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + securityContext: + capabilities: + add: + - NET_ADMIN + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + securityContext: + runAsUser: 568 + runAsGroup: 568 + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + privileged: false + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + capabilities: + add: + - NET_ADMIN + drop: + - ALL + + - it: should override the securityContext capabilities.drop + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + securityContext: + capabilities: + drop: + - NET_ADMIN + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + securityContext: + runAsUser: 568 + runAsGroup: 568 + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + privileged: false + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + capabilities: + add: [] + drop: + - NET_ADMIN + + - it: should override the securityContext seccompProfile.type + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + securityContext: + seccompProfile: + type: Unconfined + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + securityContext: + runAsUser: 568 + runAsGroup: 568 + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + privileged: false + runAsNonRoot: true + seccompProfile: + type: Unconfined + capabilities: + add: [] + drop: + - ALL + + - it: should override the securityContext all + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + readOnlyRootFilesystem: false + allowPrivilegeEscalation: true + privileged: true + runAsNonRoot: false + capabilities: + add: + - NET_ADMIN + drop: + - NET_BIND_SERVICE + seccompProfile: + type: Localhost + profile: path/to/profile.json + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + readOnlyRootFilesystem: false + allowPrivilegeEscalation: true + privileged: true + runAsNonRoot: false + seccompProfile: + type: Localhost + localhostProfile: path/to/profile.json + capabilities: + add: + - NET_ADMIN + drop: + - NET_BIND_SERVICE + + # Failures + - it: should fail with empty securityContext + set: + image: *image + securityContext: + container: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - failedTemplate: + errorMessage: Container - Expected non-empty <.Values.securityContext.container> + + - it: should fail with runAsNonRoot not a bool + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + securityContext: + runAsNonRoot: "true" + asserts: + - failedTemplate: + errorMessage: Container - Expected to be [bool], but got [true] of type [string] + + - it: should fail with readOnlyRootFilesystem not a bool + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + securityContext: + readOnlyRootFilesystem: "true" + asserts: + - failedTemplate: + errorMessage: Container - Expected to be [bool], but got [true] of type [string] + + - it: should fail with allowPrivilegeEscalation not a bool + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + securityContext: + allowPrivilegeEscalation: "true" + asserts: + - failedTemplate: + errorMessage: Container - Expected to be [bool], but got [true] of type [string] + + - it: should fail with privileged not a bool + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + securityContext: + privileged: "true" + asserts: + - failedTemplate: + errorMessage: Container - Expected to be [bool], but got [true] of type [string] + + - it: should fail with runAsUser not an int + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + securityContext: + runAsUser: "568" + asserts: + - failedTemplate: + errorMessage: Container - Expected to be [int], but got [568] of type [string] + + - it: should fail with runAsGroup not an int + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + securityContext: + runAsGroup: "568" + asserts: + - failedTemplate: + errorMessage: Container - Expected to be [int], but got [568] of type [string] + + - it: should fail without seccompProfile + set: + image: *image + securityContext: + container: + seccompProfile: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - failedTemplate: + errorMessage: Container - Expected to be defined + + - it: should fail with invalid seccompProfile + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + securityContext: + seccompProfile: + type: invalid + asserts: + - failedTemplate: + errorMessage: Container - Expected to be one of [RuntimeDefault, Localhost, Unconfined], but got [invalid] + + - it: should fail without profile on seccompProfile Localhost + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + securityContext: + seccompProfile: + type: Localhost + profile: "" + asserts: + - failedTemplate: + errorMessage: Container - Expected to be defined on type [Localhost] + + - it: should fail without capabilities + set: + image: *image + securityContext: + container: + capabilities: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - failedTemplate: + errorMessage: Container - Expected to be defined + + - it: should fail capabilities.add not a list + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + securityContext: + capabilities: + add: invalid + asserts: + - failedTemplate: + errorMessage: Container - Expected to be [list], but got [string] + + - it: should fail capabilities.drop not a list + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + securityContext: + capabilities: + drop: invalid + asserts: + - failedTemplate: + errorMessage: Container - Expected to be [list], but got [string] + + - it: should fail when trying to run as non root but with root user + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + securityContext: + runAsNonRoot: true + runAsUser: 0 + asserts: + - failedTemplate: + errorMessage: Container - Expected to be [false] with either [runAsUser, runAsGroup] set to [0] + + - it: should fail when trying to run as non root but with root group + set: + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + securityContext: + runAsNonRoot: true + runAsGroup: 0 + asserts: + - failedTemplate: + errorMessage: Container - Expected to be [false] with either [runAsUser, runAsGroup] set to [0] diff --git a/library/common-test/tests/container/termination_test.yaml b/library/common-test/tests/container/termination_test.yaml new file mode 100644 index 0000000000..57a3d93238 --- /dev/null +++ b/library/common-test/tests/container/termination_test.yaml @@ -0,0 +1,143 @@ +suite: container termination test +templates: + - common.yaml +tests: + - it: should pass with termination set + set: + some_path: /dev/termination-log + some_policy: File + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: &probes + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + termination: + messagePath: "{{ .Values.some_path }}" + messagePolicy: "{{ .Values.some_policy }}" + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + + - it: should pass with termination path only + set: + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + termination: + messagePath: /some/path + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + terminationMessagePath: /some/path + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.containers[0].terminationMessagePolicy + + - it: should pass with termination policy only + set: + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + termination: + messagePolicy: File + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + terminationMessagePolicy: File + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.containers[0].terminationMessagePath + + # Failures + - it: should fail with invalid policy + set: + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + termination: + messagePolicy: SomeInvalidPolicy + asserts: + - failedTemplate: + errorMessage: Container - Expected to be one of [File, FallbackToLogsOnError], but got [SomeInvalidPolicy] diff --git a/library/common-test/tests/container/tty_stdin_test.yaml b/library/common-test/tests/container/tty_stdin_test.yaml new file mode 100644 index 0000000000..a2ab64f1ea --- /dev/null +++ b/library/common-test/tests/container/tty_stdin_test.yaml @@ -0,0 +1,72 @@ +suite: container tty and stdin test +templates: + - common.yaml +tests: + - it: should pass without tty and stdin + set: + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: &probes + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + tty: false + stdin: false + + - it: should pass with tty and stdin enabled + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + tty: true + stdin: true + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.containers[0] + content: + tty: true + stdin: true diff --git a/library/common-test/tests/container/validation_test.yaml b/library/common-test/tests/container/validation_test.yaml new file mode 100644 index 0000000000..e6929065f8 --- /dev/null +++ b/library/common-test/tests/container/validation_test.yaml @@ -0,0 +1,41 @@ +suite: container validation test +templates: + - common.yaml +tests: + - it: should fail with more than one primary container on a workload + set: + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + container-name2: + enabled: true + primary: true + asserts: + - failedTemplate: + errorMessage: Container - Only one container can be primary per workload + + - it: should fail with no primary container on a workload + set: + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: false + container-name2: + enabled: true + primary: false + asserts: + - failedTemplate: + errorMessage: Container - At least one enabled container must be primary per workload diff --git a/library/common-test/tests/container/volumeMounts_test.yaml b/library/common-test/tests/container/volumeMounts_test.yaml new file mode 100644 index 0000000000..bb26c8995a --- /dev/null +++ b/library/common-test/tests/container/volumeMounts_test.yaml @@ -0,0 +1,709 @@ +suite: container volumeMounts test +templates: + - common.yaml +tests: + - it: should pass with shared volume on multiple workloads and containers with targetSelectAll + set: + some_path: /some/path + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: &probes + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + container-name2: + enabled: true + primary: false + imageSelector: image + probes: *probes + workload-name2: + enabled: true + primary: false + type: Job + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + container-name2: + enabled: true + primary: false + imageSelector: image + probes: *probes + persistence: + shared-vol: + enabled: true + type: emptyDir + mountPath: "{{ .Values.some_path }}" + targetSelectAll: true + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: shared-vol + mountPath: /some/path + readOnly: false + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[1].volumeMounts + content: + name: shared-vol + mountPath: /some/path + readOnly: false + - documentIndex: &jobDoc 1 + isKind: + of: Job + - documentIndex: *jobDoc + isAPIVersion: + of: batch/v1 + - documentIndex: *jobDoc + contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: shared-vol + mountPath: /some/path + readOnly: false + - documentIndex: *jobDoc + contains: + path: spec.template.spec.containers[1].volumeMounts + content: + name: shared-vol + mountPath: /some/path + readOnly: false + + - it: should pass with volume on primary workload and container + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + container-name2: + enabled: true + primary: false + imageSelector: image + probes: *probes + workload-name2: + enabled: true + primary: false + type: Job + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + container-name2: + enabled: true + primary: false + imageSelector: image + probes: *probes + persistence: + some-vol: + enabled: true + type: emptyDir + mountPath: /some/path + readOnly: true + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: some-vol + mountPath: /some/path + readOnly: true + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.containers[1].volumeMounts + - documentIndex: &jobDoc 1 + isKind: + of: Job + - documentIndex: *jobDoc + isAPIVersion: + of: batch/v1 + - documentIndex: *jobDoc + isNull: + path: spec.template.spec.containers[0].volumeMounts + - documentIndex: *jobDoc + isNull: + path: spec.template.spec.containers[1].volumeMounts + + - it: should pass with volume with selected pod and container + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + container-name2: + enabled: true + primary: false + imageSelector: image + probes: *probes + workload-name2: + enabled: true + primary: false + type: Job + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + container-name2: + enabled: true + primary: false + imageSelector: image + probes: *probes + persistence: + some-vol: + enabled: true + type: emptyDir + mountPath: /some/path + readOnly: true + targetSelector: + workload-name: + container-name2: {} + workload-name2: + container-name1: {} + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.containers[0].volumeMounts + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[1].volumeMounts + content: + name: some-vol + mountPath: /some/path + readOnly: true + - documentIndex: &jobDoc 1 + isKind: + of: Job + - documentIndex: *jobDoc + isAPIVersion: + of: batch/v1 + - documentIndex: *jobDoc + contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: some-vol + mountPath: /some/path + readOnly: true + - documentIndex: *jobDoc + isNull: + path: spec.template.spec.containers[1].volumeMounts + + - it: should pass with volume with selected pod and multiple containers + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + container-name2: + enabled: true + primary: false + imageSelector: image + probes: *probes + workload-name2: + enabled: true + primary: false + type: Job + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + container-name2: + enabled: true + primary: false + imageSelector: image + probes: *probes + persistence: + some-vol: + enabled: true + type: emptyDir + mountPath: /some/path + readOnly: true + targetSelector: + workload-name: + container-name1: {} + container-name2: {} + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: some-vol + mountPath: /some/path + readOnly: true + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[1].volumeMounts + content: + name: some-vol + mountPath: /some/path + readOnly: true + - documentIndex: &jobDoc 1 + isKind: + of: Job + - documentIndex: *jobDoc + isAPIVersion: + of: batch/v1 + - documentIndex: *jobDoc + isNull: + path: spec.template.spec.containers[0].volumeMounts + - documentIndex: *jobDoc + isNull: + path: spec.template.spec.containers[1].volumeMounts + + - it: should pass with volume with selected pod and containers and specific values + set: + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + container-name2: + enabled: true + primary: false + imageSelector: image + probes: *probes + workload-name2: + enabled: true + primary: false + type: Job + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + container-name2: + enabled: true + primary: false + imageSelector: image + probes: *probes + persistence: + some-vol: + enabled: true + type: emptyDir + mountPath: /some/path + readOnly: true + targetSelector: + workload-name: + container-name1: + mountPath: /some/other/path + readOnly: false + mountPropagation: None + subPath: /some/sub/path + container-name2: {} + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: some-vol + mountPath: /some/other/path + readOnly: false + mountPropagation: None + subPath: /some/sub/path + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[1].volumeMounts + content: + name: some-vol + mountPath: /some/path + readOnly: true + - documentIndex: &jobDoc 1 + isKind: + of: Job + - documentIndex: *jobDoc + isAPIVersion: + of: batch/v1 + - documentIndex: *jobDoc + isNull: + path: spec.template.spec.containers[0].volumeMounts + - documentIndex: *jobDoc + isNull: + path: spec.template.spec.containers[1].volumeMounts + + - it: should pass with volume with selected pod and containers and specific values from tpl + set: + some_path: /some/other/path + some_propagation: None + some_sub_path: /some/sub/path + image: *image + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + container-name2: + enabled: true + primary: false + imageSelector: image + probes: *probes + persistence: + some-vol: + enabled: true + type: emptyDir + mountPath: /some/path + readOnly: true + targetSelector: + workload-name: + container-name1: + mountPath: "{{ .Values.some_path }}" + readOnly: false + mountPropagation: "{{ .Values.some_propagation }}" + subPath: "{{ .Values.some_sub_path }}" + container-name2: {} + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: some-vol + mountPath: /some/other/path + readOnly: false + mountPropagation: None + subPath: /some/sub/path + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[1].volumeMounts + content: + name: some-vol + mountPath: /some/path + readOnly: true + + - it: should pass with cert mounted as volume with subPath + set: + image: *image + ixCertificates: + "1": + certificate: some_cert + key: some_key + scaleCertificate: + cert-name: + enabled: false + id: 1 + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + persistence: + cert-vol: + enabled: true + type: secret + objectName: cert-name + readOnly: true + targetSelector: + workload-name: + container-name1: + mountPath: /some/path/cert.crt + readOnly: true + subPath: cert.crt + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: cert-vol + mountPath: /some/path/cert.crt + readOnly: true + subPath: cert.crt + + - it: should pass with cert mounted as volume with subPath + set: + image: *image + ixCertificates: + "1": + certificate: some_cert + key: some_key + scaleCertificate: + cert-name: + enabled: false + id: 1 + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + persistence: + cert-vol: + enabled: true + type: secret + objectName: cert-name + readOnly: true + items: + - key: tls.crt + path: cert.crt + targetSelector: + workload-name: + container-name1: + mountPath: /some/path + readOnly: true + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: cert-vol + mountPath: /some/path + readOnly: true + + # Failures + - it: should fail with invalid mountPropagation + set: + image: *image + workload: + workload-name: &workload + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + persistence: + vol-name: + enabled: true + type: emptyDir + mountPath: /some/path + mountPropagation: HostToContainer + targetSelector: + workload-name: + container-name1: + mountPropagation: invalid + asserts: + - failedTemplate: + errorMessage: Persistence - Expected to be one of [None, HostToContainer, Bidirectional], but got [invalid] + + - it: should fail with non-boolean readOnly + set: + image: *image + workload: + workload-name: *workload + persistence: + vol-name: + enabled: true + type: emptyDir + mountPath: /some/path + targetSelector: + workload-name: + container-name1: + readOnly: invalid + asserts: + - failedTemplate: + errorMessage: Persistence - Expected to be [boolean], but got [string] + + - it: should fail with empty readOnly + set: + image: *image + workload: + workload-name: *workload + persistence: + vol-name: + enabled: true + type: emptyDir + mountPath: /some/path + targetSelector: + workload-name: + container-name1: + readOnly: + asserts: + - failedTemplate: + errorMessage: Persistence - Expected to be [boolean], but got [invalid] + + - it: should fail with empty mountPath + set: + image: *image + workload: + workload-name: *workload + persistence: + vol-name: + enabled: true + type: emptyDir + mountPath: "" + targetSelector: + workload-name: + container-name1: {} + asserts: + - failedTemplate: + errorMessage: Persistence - Expected non-empty + + - it: should fail with mountPath not starting with / + set: + image: *image + workload: + workload-name: *workload + persistence: + vol-name: + enabled: true + type: emptyDir + targetSelector: + workload-name: + container-name1: + mountPath: some/path + asserts: + - failedTemplate: + errorMessage: Persistence - Expected to start with a forward slash [/] + + - it: should fail with non-dict targetSelect.workloadName + set: + image: *image + workload: + workload-name: *workload + persistence: + vol-name: + enabled: true + type: emptyDir + targetSelector: + workload-name: string + asserts: + - failedTemplate: + errorMessage: Persistence - Expected to be a [dict], but got [string] + + - it: should fail with empty targetSelect.workloadName + set: + image: *image + workload: + workload-name: *workload + persistence: + vol-name: + enabled: true + type: emptyDir + targetSelector: + workload-name: {} + asserts: + - failedTemplate: + errorMessage: Persistence - Expected non-empty diff --git a/library/common-test/tests/cronjob/metadata_test.yaml b/library/common-test/tests/cronjob/metadata_test.yaml new file mode 100644 index 0000000000..e707913f46 --- /dev/null +++ b/library/common-test/tests/cronjob/metadata_test.yaml @@ -0,0 +1,85 @@ +suite: cronjob metadata test +templates: + - common.yaml +chart: + appVersion: &appVer v9.9.9 +tests: + - it: should pass with cronjob created with labels and annotations + set: + label1: label1 + label2: global_label2 + annotation1: annotation1 + annotation2: global_annotation2 + global: + labels: + g_label1: global_label1 + g_label2: "{{ .Values.label2 }}" + annotations: + g_annotation1: global_annotation1 + g_annotation2: "{{ .Values.annotation2 }}" + workload: + workload-name: + enabled: true + primary: true + type: CronJob + schedule: "*/1 * * * *" + labels: + label1: "{{ .Values.label1 }}" + label2: label2 + annotations: + annotation1: "{{ .Values.annotation1 }}" + annotation2: annotation2 + podSpec: + labels: + pod-label1: pod-label1 + pod-label2: "{{ .Values.label2 }}" + annotations: + pod-annotation1: pod-annotation1 + pod-annotation2: "{{ .Values.annotation2 }}" + asserts: + - documentIndex: &cronJobDoc 0 + isKind: + of: CronJob + - documentIndex: *cronJobDoc + equal: + path: metadata.annotations + value: + annotation1: annotation1 + annotation2: annotation2 + g_annotation1: global_annotation1 + g_annotation2: global_annotation2 + - documentIndex: *cronJobDoc + equal: + path: metadata.labels + value: + app: common-test-1.0.0 + release: RELEASE-NAME + helm-revision: "0" + helm.sh/chart: common-test-1.0.0 + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: *appVer + g_label1: global_label1 + g_label2: global_label2 + label1: label1 + label2: label2 + - documentIndex: *cronJobDoc + equal: + path: spec.jobTemplate.spec.template.metadata.labels + value: + pod.name: workload-name + app: common-test-1.0.0 + release: RELEASE-NAME + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: common-test + app.kubernetes.io/version: v9.9.9 + helm-revision: "0" + helm.sh/chart: common-test-1.0.0 + g_label1: global_label1 + g_label2: global_label2 + pod-label1: pod-label1 + pod-label2: global_label2 + - documentIndex: *cronJobDoc + matchRegex: + path: spec.jobTemplate.spec.template.metadata.annotations.rollme + pattern: '^[0-9a-zA-Z]{5}$' diff --git a/library/common-test/tests/cronjob/spec_test.yaml b/library/common-test/tests/cronjob/spec_test.yaml new file mode 100644 index 0000000000..6f75456779 --- /dev/null +++ b/library/common-test/tests/cronjob/spec_test.yaml @@ -0,0 +1,83 @@ +suite: cronjob spec test +templates: + - common.yaml +tests: + - it: should pass with workload enabled + set: + workload: + workload-name: + enabled: true + primary: true + type: CronJob + schedule: "* * * * *" + podSpec: {} + asserts: + - documentIndex: &cronJobDoc 0 + isKind: + of: CronJob + - documentIndex: *cronJobDoc + isAPIVersion: + of: batch/v1 + - documentIndex: *cronJobDoc + isSubset: + path: spec + content: + schedule: "* * * * *" + timeZone: UTC + concurrencyPolicy: Forbid + failedJobsHistoryLimit: 1 + successfulJobsHistoryLimit: 3 + startingDeadlineSeconds: + - documentIndex: *cronJobDoc + isSubset: + path: spec.jobTemplate.spec + content: + backoffLimit: 5 + completionMode: NonIndexed + completions: + parallelism: 1 + ttlSecondsAfterFinished: 120 + + - it: should apply spec correctly + set: + cron: "*/5 * * * *" + someTimezone: America/New_York + workload: + workload-name: + enabled: true + primary: true + type: CronJob + schedule: "{{ .Values.cron }}" + timezone: "{{ .Values.someTimezone }}" + concurrencyPolicy: Allow + failedJobsHistoryLimit: 2 + successfulJobsHistoryLimit: 4 + startingDeadlineSeconds: 100 + backoffLimit: 5 + completionMode: Indexed + completions: 5 + parallelism: 5 + ttlSecondsAfterFinished: 100 + activeDeadlineSeconds: 100 + podSpec: {} + asserts: + - documentIndex: *cronJobDoc + isSubset: + path: spec + content: + schedule: "*/5 * * * *" + concurrencyPolicy: Allow + failedJobsHistoryLimit: 2 + successfulJobsHistoryLimit: 4 + startingDeadlineSeconds: 100 + timeZone: America/New_York + - documentIndex: *cronJobDoc + isSubset: + path: spec.jobTemplate.spec + content: + backoffLimit: 5 + completionMode: Indexed + completions: 5 + parallelism: 5 + ttlSecondsAfterFinished: 100 + activeDeadlineSeconds: 100 diff --git a/library/common-test/tests/cronjob/validation_test.yaml b/library/common-test/tests/cronjob/validation_test.yaml new file mode 100644 index 0000000000..77c1771fb0 --- /dev/null +++ b/library/common-test/tests/cronjob/validation_test.yaml @@ -0,0 +1,43 @@ +suite: cronjob validation test +templates: + - common.yaml +tests: + - it: should fail with invalid concurrencyPolicy + set: + workload: + workload-name: + enabled: true + primary: true + type: CronJob + concurrencyPolicy: not-a-policy + podSpec: {} + asserts: + - failedTemplate: + errorMessage: CronJob - Expected to be one of [Allow, Forbid, Replace], but got [not-a-policy] + + - it: should fail with empty schedule + set: + workload: + workload-name: + enabled: true + primary: true + type: CronJob + schedule: + podSpec: {} + asserts: + - failedTemplate: + errorMessage: CronJob - Expected non-empty + + - it: should fail with invalid completionMode (make sure job validation kicks in) + set: + workload: + workload-name: + enabled: true + primary: true + type: CronJob + schedule: "* * * * *" + completionMode: not-a-mode + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Job - Expected to be one of [Indexed, NonIndexed], but got [not-a-mode] diff --git a/library/common-test/tests/deployment/metadata_test.yaml b/library/common-test/tests/deployment/metadata_test.yaml new file mode 100644 index 0000000000..50110ded51 --- /dev/null +++ b/library/common-test/tests/deployment/metadata_test.yaml @@ -0,0 +1,148 @@ +suite: deployment metadata test +templates: + - common.yaml +chart: + appVersion: &appVer v9.9.9 +tests: + - it: should pass with deployment created with labels and annotations + set: + label1: label1 + label2: global_label2 + annotation1: annotation1 + annotation2: global_annotation2 + global: + labels: + g_label1: global_label1 + g_label2: "{{ .Values.label2 }}" + annotations: + g_annotation1: global_annotation1 + g_annotation2: "{{ .Values.annotation2 }}" + workload: + workload-name: + enabled: true + primary: true + type: Deployment + labels: + label1: "{{ .Values.label1 }}" + label2: label2 + annotations: + annotation1: "{{ .Values.annotation1 }}" + annotation2: annotation2 + podSpec: + labels: + pod-label1: pod-label1 + pod-label2: "{{ .Values.label2 }}" + annotations: + pod-annotation1: pod-annotation1 + pod-annotation2: "{{ .Values.annotation2 }}" + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + equal: + path: metadata.annotations + value: + annotation1: annotation1 + annotation2: annotation2 + g_annotation1: global_annotation1 + g_annotation2: global_annotation2 + - documentIndex: *deploymentDoc + equal: + path: metadata.labels + value: + app: common-test-1.0.0 + release: RELEASE-NAME + helm-revision: "0" + helm.sh/chart: common-test-1.0.0 + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: *appVer + g_label1: global_label1 + g_label2: global_label2 + label1: label1 + label2: label2 + - documentIndex: *deploymentDoc + equal: + path: spec.selector.matchLabels + value: + pod.name: workload-name + app.kubernetes.io/name: common-test + app.kubernetes.io/instance: RELEASE-NAME + - documentIndex: *deploymentDoc + equal: + path: spec.template.metadata.labels + value: + pod.name: workload-name + app: common-test-1.0.0 + release: RELEASE-NAME + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: common-test + app.kubernetes.io/version: v9.9.9 + helm-revision: "0" + helm.sh/chart: common-test-1.0.0 + g_label1: global_label1 + g_label2: global_label2 + pod-label1: pod-label1 + pod-label2: global_label2 + - documentIndex: *deploymentDoc + matchRegex: + path: spec.template.metadata.annotations.rollme + pattern: '^[0-9a-zA-Z]{5}$' + + - it: should pass with 2 deployment created with correct selector labels + set: + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: {} + other-workload-name: + enabled: true + primary: false + type: Deployment + podSpec: {} + asserts: + - documentIndex: *deploymentDoc + isKind: + of: Deployment + - documentIndex: *deploymentDoc + equal: + path: metadata.name + value: release-name-common-test-other-workload-name + - documentIndex: *deploymentDoc + equal: + path: spec.selector.matchLabels + value: + pod.name: other-workload-name + app.kubernetes.io/name: common-test + app.kubernetes.io/instance: RELEASE-NAME + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.metadata.labels + content: + pod.name: other-workload-name + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/name: common-test + - documentIndex: &otherDeploymentDoc 1 + isKind: + of: Deployment + - documentIndex: *otherDeploymentDoc + equal: + path: metadata.name + value: release-name-common-test + - documentIndex: *otherDeploymentDoc + equal: + path: spec.selector.matchLabels + value: + pod.name: workload-name + app.kubernetes.io/name: common-test + app.kubernetes.io/instance: RELEASE-NAME + - documentIndex: *otherDeploymentDoc + isSubset: + path: spec.template.metadata.labels + content: + pod.name: workload-name + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/name: common-test diff --git a/library/common-test/tests/deployment/spec_test.yaml b/library/common-test/tests/deployment/spec_test.yaml new file mode 100644 index 0000000000..d525cce779 --- /dev/null +++ b/library/common-test/tests/deployment/spec_test.yaml @@ -0,0 +1,96 @@ +suite: deployment spec test +templates: + - common.yaml +tests: + - it: should pass with workload enabled + set: + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec + content: + replicas: 1 + revisionHistoryLimit: 3 + strategy: + type: Recreate + + - it: should apply spec correctly + set: + workload: + workload-name: + enabled: true + primary: true + type: Deployment + replicas: 2 + revisionHistoryLimit: 4 + strategy: RollingUpdate + rollingUpdate: + maxSurge: 5 + maxUnavailable: 5 + podSpec: {} + asserts: + - documentIndex: *deploymentDoc + isSubset: + path: spec + content: + replicas: 2 + revisionHistoryLimit: 4 + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 5 + maxUnavailable: 5 + + - it: should apply maxSurge with 0 + set: + workload: + workload-name: + enabled: true + primary: true + type: Deployment + strategy: RollingUpdate + rollingUpdate: + maxSurge: 0 + podSpec: {} + asserts: + - documentIndex: *deploymentDoc + isSubset: + path: spec + content: + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 0 + + - it: should apply maxUnavailable with 0 + set: + workload: + workload-name: + enabled: true + primary: true + type: Deployment + strategy: RollingUpdate + rollingUpdate: + maxUnavailable: 0 + podSpec: {} + asserts: + - documentIndex: *deploymentDoc + isSubset: + path: spec + content: + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 0 diff --git a/library/common-test/tests/deployment/validation_test.yaml b/library/common-test/tests/deployment/validation_test.yaml new file mode 100644 index 0000000000..2d08f3cc50 --- /dev/null +++ b/library/common-test/tests/deployment/validation_test.yaml @@ -0,0 +1,30 @@ +suite: deployment validation test +templates: + - common.yaml +tests: + - it: should fail with invalid strategy + set: + workload: + workload-name: + enabled: true + primary: true + type: Deployment + strategy: not-a-strategy + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Deployment - Expected to be one of [Recreate, RollingUpdate], but got [not-a-strategy] + + - it: should fail with rollingUpdate not a dict + set: + workload: + workload-name: + enabled: true + primary: true + type: Deployment + strategy: RollingUpdate + rollingUpdate: "not a dict" + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Deployment - Expected to be a dictionary, but got [string] diff --git a/library/common-test/tests/externalInterface/metadata_test.yaml b/library/common-test/tests/externalInterface/metadata_test.yaml new file mode 100644 index 0000000000..171f7a4aa5 --- /dev/null +++ b/library/common-test/tests/externalInterface/metadata_test.yaml @@ -0,0 +1,238 @@ +suite: externalInterface metadata test +templates: + - common.yaml +release: + name: release-name +tests: + - it: should generate correct annotations without selector + set: + # Simulate middleware injection + ixExternalInterfacesConfiguration: + - '{"cniVersion": "0.3.1", "name": "ix-release-name-0", "type": "macvlan", "master": "ens3s0", "ipam": {"type": "dhcp"}}' + - '{"cniVersion": "0.3.1", "name": "ix-release-name-0", "type": "macvlan", "master": "ens4s0", "ipam": {"type": "dhcp"}}' + ixExternalInterfacesConfigurationNames: + - ix-release-name-0 + - ix-release-name-1 + scaleExternalInterface: + - hostInterface: enp0s3 + ipam: + type: dhcp + - hostInterface: enp0s4 + ipam: + type: dhcp + image: &image + repository: nginx + tag: 1.21.4 + pullPolicy: IfNotPresent + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: &probes + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + workload-name2: + enabled: true + primary: false + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - documentIndex: &deploymentDoc 2 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + equal: + path: metadata.name + value: release-name-common-test + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.metadata.annotations + content: + k8s.v1.cni.cncf.io/networks: ix-release-name-0, ix-release-name-1 + - documentIndex: &otherDeploymentDoc 3 + isKind: + of: Deployment + - documentIndex: *otherDeploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *otherDeploymentDoc + equal: + path: metadata.name + value: release-name-common-test-workload-name2 + - documentIndex: *otherDeploymentDoc + isNotSubset: + path: spec.template.metadata.annotations + content: + k8s.v1.cni.cncf.io/networks: ix-release-name-0, ix-release-name-1 + + - it: should generate correct annotations with targetSelectAll + set: + # Simulate middleware injection + ixExternalInterfacesConfiguration: + - '{"cniVersion": "0.3.1", "name": "ix-release-name-0", "type": "macvlan", "master": "ens3s0", "ipam": {"type": "dhcp"}}' + - '{"cniVersion": "0.3.1", "name": "ix-release-name-0", "type": "macvlan", "master": "ens4s0", "ipam": {"type": "dhcp"}}' + ixExternalInterfacesConfigurationNames: + - ix-release-name-0 + - ix-release-name-1 + scaleExternalInterface: + - hostInterface: enp0s3 + ipam: + type: dhcp + targetSelectAll: true + - hostInterface: enp0s4 + ipam: + type: dhcp + targetSelectAll: true + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + workload-name2: + enabled: true + primary: false + type: Job + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - documentIndex: *deploymentDoc + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + equal: + path: metadata.name + value: release-name-common-test + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.metadata.annotations + content: + k8s.v1.cni.cncf.io/networks: ix-release-name-0, ix-release-name-1 + - documentIndex: &jobDoc 3 + isKind: + of: Job + - documentIndex: *jobDoc + isAPIVersion: + of: batch/v1 + - documentIndex: *jobDoc + equal: + path: metadata.name + value: release-name-common-test-workload-name2 + - documentIndex: *jobDoc + isSubset: + path: spec.template.metadata.annotations + content: + k8s.v1.cni.cncf.io/networks: ix-release-name-0, ix-release-name-1 + + - it: should generate correct annotations with targetSelector + set: + # Simulate middleware injection + ixExternalInterfacesConfiguration: + - '{"cniVersion": "0.3.1", "name": "ix-release-name-0", "type": "macvlan", "master": "ens3s0", "ipam": {"type": "dhcp"}}' + - '{"cniVersion": "0.3.1", "name": "ix-release-name-0", "type": "macvlan", "master": "ens4s0", "ipam": {"type": "dhcp"}}' + ixExternalInterfacesConfigurationNames: + - ix-release-name-0 + - ix-release-name-1 + scaleExternalInterface: + - hostInterface: enp0s3 + ipam: + type: dhcp + targetSelector: + - workload-name1 + - workload-name2 + - hostInterface: enp0s4 + ipam: + type: dhcp + targetSelector: + - workload-name1 + image: *image + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + workload-name2: + enabled: true + primary: false + type: CronJob + schedule: "*/1 * * * *" + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: *probes + asserts: + - documentIndex: *deploymentDoc + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + equal: + path: metadata.name + value: release-name-common-test + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.metadata.annotations + content: + k8s.v1.cni.cncf.io/networks: ix-release-name-0, ix-release-name-1 + - documentIndex: &cronJobDoc 3 + isKind: + of: CronJob + - documentIndex: *cronJobDoc + isAPIVersion: + of: batch/v1 + - documentIndex: *cronJobDoc + equal: + path: metadata.name + value: release-name-common-test-workload-name2 + - documentIndex: *cronJobDoc + isSubset: + path: spec.jobTemplate.spec.template.metadata.annotations + content: + k8s.v1.cni.cncf.io/networks: ix-release-name-0 diff --git a/library/common-test/tests/externalInterface/name_test.yaml b/library/common-test/tests/externalInterface/name_test.yaml new file mode 100644 index 0000000000..e6a39b9ec7 --- /dev/null +++ b/library/common-test/tests/externalInterface/name_test.yaml @@ -0,0 +1,43 @@ +suite: externalInterface name test +templates: + - common.yaml +release: + name: release-name +tests: + - it: should generate correct name NetworkAttachmentDefinition + set: + # Simulate middleware injection + ixExternalInterfacesConfiguration: + - '{"cniVersion": "0.3.1", "name": "ix-release-name-0", "type": "macvlan", "master": "ens3s0", "ipam": {"type": "dhcp"}}' + - '{"cniVersion": "0.3.1", "name": "ix-release-name-0", "type": "macvlan", "master": "ens4s0", "ipam": {"type": "dhcp"}}' + ixExternalInterfacesConfigurationNames: + - ix-release-name-0 + - ix-release-name-1 + scaleExternalInterface: + - hostInterface: enp0s3 + ipam: + type: dhcp + - hostInterface: enp0s4 + ipam: + type: dhcp + asserts: + - documentIndex: &networkDoc 0 + isKind: + of: NetworkAttachmentDefinition + - documentIndex: *networkDoc + isAPIVersion: + of: k8s.cni.cncf.io/v1 + - documentIndex: *networkDoc + equal: + path: metadata.name + value: ix-release-name-0 + - documentIndex: &otherNetworkDoc 1 + isKind: + of: NetworkAttachmentDefinition + - documentIndex: *otherNetworkDoc + isAPIVersion: + of: k8s.cni.cncf.io/v1 + - documentIndex: *otherNetworkDoc + equal: + path: metadata.name + value: ix-release-name-1 diff --git a/library/common-test/tests/externalInterface/validation_test.yaml b/library/common-test/tests/externalInterface/validation_test.yaml new file mode 100644 index 0000000000..0ebaaae149 --- /dev/null +++ b/library/common-test/tests/externalInterface/validation_test.yaml @@ -0,0 +1,137 @@ +suite: external interface validation test +templates: + - common.yaml +release: + name: release-name +tests: + - it: should fail with targetSelector not a list + set: + scaleExternalInterface: + - targetSelector: "not a list" + asserts: + - failedTemplate: + errorMessage: External Interface - Expected to be a [list], but got [string] + + - it: should fail with empty hostInterface + set: + scaleExternalInterface: + - hostInterface: "" + asserts: + - failedTemplate: + errorMessage: External Interface - Expected non-empty + + - it: should fail with empty ipam + set: + scaleExternalInterface: + - hostInterface: enp0s3 + ipam: {} + asserts: + - failedTemplate: + errorMessage: External Interface - Expected non-empty + + - it: should fail with empty ipam.type + set: + scaleExternalInterface: + - hostInterface: enp0s3 + ipam: + type: "" + asserts: + - failedTemplate: + errorMessage: External Interface - Expected non-empty + + - it: should fail with invalid ipam.type + set: + scaleExternalInterface: + - hostInterface: enp0s3 + ipam: + type: invalid + asserts: + - failedTemplate: + errorMessage: External Interface - Expected to be one of [dhcp, static], but got [invalid] + + - it: should fail with non-empty staticIPConfigurations on dhcp + set: + scaleExternalInterface: + - hostInterface: enp0s3 + ipam: + type: dhcp + staticIPConfigurations: + - ipAddress: 1.2.3.4 + asserts: + - failedTemplate: + errorMessage: External Interface - Expected empty and when is not [static] + + - it: should fail with non-empty staticRoutes on dhcp + set: + scaleExternalInterface: + - hostInterface: enp0s3 + ipam: + type: dhcp + staticRoutes: + - gateway: 1.2.3.4 + destination: 1.2.3.4 + asserts: + - failedTemplate: + errorMessage: External Interface - Expected empty and when is not [static] + + - it: should fail with empty staticIPConfigurations on static + set: + scaleExternalInterface: + - hostInterface: enp0s3 + ipam: + type: static + staticIPConfigurations: [] + asserts: + - failedTemplate: + errorMessage: External Interface - Expected non-empty when is [static] + + - it: should fail with empty gateway on staticRoutes on static + set: + scaleExternalInterface: + - hostInterface: enp0s3 + ipam: + type: static + staticIPConfigurations: + - ipAddress: 1.2.3.4 + staticRoutes: + - gateway: "" + destination: 1.2.3.4 + asserts: + - failedTemplate: + errorMessage: External Interface - Expected non-empty in + + - it: should fail with empty destination on staticRoutes on static + set: + scaleExternalInterface: + - hostInterface: enp0s3 + ipam: + type: static + staticIPConfigurations: + - ipAddress: 1.2.3.4 + staticRoutes: + - gateway: 1.2.3.4 + destination: "" + asserts: + - failedTemplate: + errorMessage: External Interface - Expected non-empty in + + - it: should fail with empty ixExternalInterfaceConfigurationNames when interface is defined + set: + # Simulate middleware injection + ixExternalInterfacesConfiguration: + - '{"cniVersion": "0.3.1", "name": "ix-release-name-0", "type": "macvlan", "master": "ens3s0", "ipam": {"type": "dhcp"}}' + ixExternalInterfaceConfigurationNames: [] + scaleExternalInterface: + - hostInterface: enp0s3 + ipam: + type: dhcp + workload: + workload-name1: + enabled: true + primary: true + type: CronJob + schedule: "*/1 * * * *" + podSpec: {} + asserts: + - failedTemplate: + errorMessage: External Interface - Expected non empty diff --git a/library/common-test/tests/imagePullSecret/data_test.yaml b/library/common-test/tests/imagePullSecret/data_test.yaml new file mode 100644 index 0000000000..cdf40653e6 --- /dev/null +++ b/library/common-test/tests/imagePullSecret/data_test.yaml @@ -0,0 +1,48 @@ +suite: imagePullSecret data test +templates: + - common.yaml +tests: + - it: should pass with data + set: + imagePullSecret: + my-secret1: + enabled: true + data: + registry: reg + username: user + password: pass + email: mail + asserts: + - documentIndex: &secretDoc 0 + isKind: + of: Secret + - documentIndex: *secretDoc + equal: + path: data + value: + .dockerconfigjson: eyJhdXRocyI6eyJyZWdpc3RyeSI6eyJhdXRoIjoiZFhObGNqcHdZWE56IiwiZW1haWwiOiJtYWlsIiwicGFzc3dvcmQiOiJwYXNzIiwidXNlcm5hbWUiOiJ1c2VyIn19fQ== + - documentIndex: *secretDoc + equal: + path: type + value: kubernetes.io/dockerconfigjson + + - it: should pass with data from tpl + set: + registry: quay.io + user: user + pass: secret_pass + email: mail@example.com + imagePullSecret: + my-secret1: + enabled: true + data: + registry: "{{ .Values.registry }}" + username: "{{ .Values.user }}" + password: "{{ .Values.pass }}" + email: "{{ .Values.email }}" + asserts: + - documentIndex: *secretDoc + equal: + path: data + value: + .dockerconfigjson: eyJhdXRocyI6eyJyZWdpc3RyeSI6eyJhdXRoIjoiZFhObGNqcHpaV055WlhSZmNHRnpjdz09IiwiZW1haWwiOiJtYWlsQGV4YW1wbGUuY29tIiwicGFzc3dvcmQiOiJzZWNyZXRfcGFzcyIsInVzZXJuYW1lIjoidXNlciJ9fX0= diff --git a/library/common-test/tests/imagePullSecret/metadata_test.yaml b/library/common-test/tests/imagePullSecret/metadata_test.yaml new file mode 100644 index 0000000000..21f60282bf --- /dev/null +++ b/library/common-test/tests/imagePullSecret/metadata_test.yaml @@ -0,0 +1,59 @@ +suite: imagePullSecret metadata test +templates: + - common.yaml +chart: + appVersion: &appVer v9.9.9 +tests: + - it: should pass with secret created with labels and annotations + set: + label1: label1 + label2: global_label2 + annotation1: annotation1 + annotation2: global_annotation2 + global: + labels: + g_label1: global_label1 + g_label2: "{{ .Values.label2 }}" + annotations: + g_annotation1: global_annotation1 + g_annotation2: "{{ .Values.annotation2 }}" + imagePullSecret: + my-secret1: + enabled: true + labels: + label1: "{{ .Values.label1 }}" + label2: label2 + annotations: + annotation1: "{{ .Values.annotation1 }}" + annotation2: annotation2 + data: + registry: reg + username: user + password: pass + email: mail + asserts: + - documentIndex: &secretDoc 0 + isKind: + of: Secret + - documentIndex: *secretDoc + equal: + path: metadata.annotations + value: + annotation1: annotation1 + annotation2: annotation2 + g_annotation1: global_annotation1 + g_annotation2: global_annotation2 + - documentIndex: *secretDoc + equal: + path: metadata.labels + value: + app: common-test-1.0.0 + release: RELEASE-NAME + helm-revision: "0" + helm.sh/chart: common-test-1.0.0 + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: *appVer + g_label1: global_label1 + g_label2: global_label2 + label1: label1 + label2: label2 diff --git a/library/common-test/tests/imagePullSecret/name_test.yaml b/library/common-test/tests/imagePullSecret/name_test.yaml new file mode 100644 index 0000000000..4b6b0988f3 --- /dev/null +++ b/library/common-test/tests/imagePullSecret/name_test.yaml @@ -0,0 +1,42 @@ +suite: imagePullSecret name test +templates: + - common.yaml +tests: + - it: should generate correct name + set: + imagePullSecret: + my-pull-secret1: + enabled: true + data: + registry: reg1 + username: user1 + password: pass1 + email: mail1 + my-pull-secret2: + enabled: true + data: + registry: reg2 + username: user2 + password: pass2 + email: mail2 + asserts: + - documentIndex: &secretDoc 0 + isKind: + of: Secret + - documentIndex: *secretDoc + isAPIVersion: + of: v1 + - documentIndex: *secretDoc + equal: + path: metadata.name + value: release-name-common-test-my-pull-secret1 + - documentIndex: &otherSecretDoc 1 + isKind: + of: Secret + - documentIndex: *otherSecretDoc + isAPIVersion: + of: v1 + - documentIndex: *otherSecretDoc + equal: + path: metadata.name + value: release-name-common-test-my-pull-secret2 diff --git a/library/common-test/tests/imagePullSecret/validation_test.yaml b/library/common-test/tests/imagePullSecret/validation_test.yaml new file mode 100644 index 0000000000..99a324f134 --- /dev/null +++ b/library/common-test/tests/imagePullSecret/validation_test.yaml @@ -0,0 +1,129 @@ +suite: imagePullSecret validation test +templates: + - common.yaml +tests: + - it: should fail with name longer than 63 characters + set: + imagePullSecret: + my-pull-secret-super-long-name-that-is-longer-than-63-characters: + enabled: true + data: &data + registry: reg + username: user + password: pass + email: mail + asserts: + - failedTemplate: + errorMessage: Name [release-name-common-test-my-pull-secret-super-long-name-that-is-longer-than-63-characters] is not valid. Must start and end with an alphanumeric lowercase character. It can contain '-'. And must be at most 63 characters. + + - it: should fail with name starting with underscore + set: + imagePullSecret: + _my-pull-secret: + enabled: true + data: *data + asserts: + - failedTemplate: + errorMessage: Name [release-name-common-test-_my-pull-secret] is not valid. Must start and end with an alphanumeric lowercase character. It can contain '-'. And must be at most 63 characters. + + - it: should fail with labels not a dict + set: + imagePullSecret: + my-pull-secret: + enabled: true + labels: "not a dict" + data: *data + asserts: + - failedTemplate: + errorMessage: Image Pull Secret - Expected to be a dictionary, but got [string] + + - it: should fail with annotations not a dict + set: + imagePullSecret: + my-pull-secret: + enabled: true + annotations: "not a dict" + data: *data + asserts: + - failedTemplate: + errorMessage: Image Pull Secret - Expected to be a dictionary, but got [string] + + - it: should fail with data not a dict + set: + imagePullSecret: + my-pull-secret: + enabled: true + data: "not a dict" + asserts: + - failedTemplate: + errorMessage: Image Pull Secret - Expected to be a dictionary, but got [string] + + - it: should fail with empty data + set: + imagePullSecret: + my-pull-secret: + enabled: true + data: {} + asserts: + - failedTemplate: + errorMessage: Image Pull Secret - Expected non-empty + + - it: should fail with empty registry key + set: + imagePullSecret: + my-pull-secret: + enabled: true + type: "" + data: + registry: "" + username: user + password: pass + email: mail + asserts: + - failedTemplate: + errorMessage: Image Pull Secret - Expected non-empty + + - it: should fail with empty username key + set: + imagePullSecret: + my-pull-secret: + enabled: true + type: "" + data: + registry: registry + username: "" + password: pass + email: mail + asserts: + - failedTemplate: + errorMessage: Image Pull Secret - Expected non-empty + + - it: should fail with empty password key + set: + imagePullSecret: + my-pull-secret: + enabled: true + type: "" + data: + registry: registry + username: user + password: "" + email: mail + asserts: + - failedTemplate: + errorMessage: Image Pull Secret - Expected non-empty + + - it: should fail with empty email key + set: + imagePullSecret: + my-pull-secret: + enabled: true + type: "" + data: + registry: registry + username: user + password: pass + email: "" + asserts: + - failedTemplate: + errorMessage: Image Pull Secret - Expected non-empty diff --git a/library/common-test/tests/initContainer/data_test.yaml b/library/common-test/tests/initContainer/data_test.yaml new file mode 100644 index 0000000000..d6e5556b0f --- /dev/null +++ b/library/common-test/tests/initContainer/data_test.yaml @@ -0,0 +1,153 @@ +suite: init container data test +templates: + - common.yaml +tests: + - it: should generate correct init container + set: + initType: install + render: true + persistence: + shared-vol: + enabled: true + type: emptyDir + mountPath: /some/path + targetSelector: + workload-name: + container-name2: {} + image: + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + initImage: + repository: bash + tag: latest + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + initContainers: + container-name1: + enabled: "{{ .Values.render }}" + type: init + imageSelector: initImage + container-name3: + enabled: true + type: upgrade + imageSelector: initImage + container-name2: + enabled: true + type: "{{ .Values.initType }}" + imageSelector: initImage + probes: + liveness: + enabled: true + readiness: + enabled: true + startup: + enabled: true + command: + - /bin/sh + - -c + - | + echo "Using image {{ .Values.initImage.repository }}" + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.initContainers[0] + content: + name: release-name-common-test-install-container-name2 + image: bash:latest + command: + - /bin/sh + - -c + - | + echo "Using image bash" + volumeMounts: + - name: shared-vol + mountPath: /some/path + readOnly: false + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.initContainers[0].livenessProbe + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.initContainers[0].readinessProbe + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.initContainers[0].startupProbe + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.initContainers[1] + content: + name: release-name-common-test-init-container-name1 + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.initContainers[1].command + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.initContainers[1].volumeMounts + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.initContainers[2] + + - it: should NOT generate render init container + set: + render: false + image: + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + initContainers: + container-name1: + enabled: "{{ .Values.render }}" + type: init + imageSelector: image + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.initContainers diff --git a/library/common-test/tests/initContainer/data_upgrade_test.yaml b/library/common-test/tests/initContainer/data_upgrade_test.yaml new file mode 100644 index 0000000000..5e99ad728e --- /dev/null +++ b/library/common-test/tests/initContainer/data_upgrade_test.yaml @@ -0,0 +1,155 @@ +suite: init container data test (upgrade) +templates: + - common.yaml +release: + upgrade: true +tests: + - it: should generate correct init container + set: + initType: upgrade + render: true + persistence: + shared-vol: + enabled: true + type: emptyDir + mountPath: /some/path + targetSelector: + workload-name: + container-name2: {} + image: + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + initImage: + repository: bash + tag: latest + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: "{{ .Values.render }}" + primary: true + imageSelector: image + probes: + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + initContainers: + container-name1: + enabled: true + type: init + imageSelector: initImage + container-name3: + enabled: true + type: install + imageSelector: initImage + container-name2: + enabled: true + type: "{{ .Values.initType }}" + imageSelector: initImage + probes: + liveness: + enabled: true + readiness: + enabled: true + startup: + enabled: true + command: + - /bin/sh + - -c + - | + echo "Using image {{ .Values.initImage.repository }}" + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.initContainers[0] + content: + name: release-name-common-test-upgrade-container-name2 + image: bash:latest + command: + - /bin/sh + - -c + - | + echo "Using image bash" + volumeMounts: + - name: shared-vol + mountPath: /some/path + readOnly: false + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.initContainers[0].livenessProbe + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.initContainers[0].readinessProbe + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.initContainers[0].startupProbe + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.initContainers[1] + content: + name: release-name-common-test-init-container-name1 + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.initContainers[1].command + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.initContainers[1].volumeMounts + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.initContainers[2] + + - it: should NOT generate render init container + set: + render: false + image: + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + initContainers: + container-name1: + enabled: "{{ .Values.render }}" + type: upgrade + imageSelector: image + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.initContainers diff --git a/library/common-test/tests/initContainer/name_test.yaml b/library/common-test/tests/initContainer/name_test.yaml new file mode 100644 index 0000000000..c0a6da421b --- /dev/null +++ b/library/common-test/tests/initContainer/name_test.yaml @@ -0,0 +1,42 @@ +suite: init container name test +templates: + - common.yaml +tests: + - it: should generate correct init container name + set: + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + initContainers: + container-name1: + enabled: true + type: init + imageSelector: image + container-name2: + enabled: true + type: install + imageSelector: image + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.initContainers[0] + content: + name: release-name-common-test-install-container-name2 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.initContainers[1] + content: + name: release-name-common-test-init-container-name1 diff --git a/library/common-test/tests/initContainer/name_upgrade_test.yaml b/library/common-test/tests/initContainer/name_upgrade_test.yaml new file mode 100644 index 0000000000..7f9514455e --- /dev/null +++ b/library/common-test/tests/initContainer/name_upgrade_test.yaml @@ -0,0 +1,44 @@ +suite: init container name test (upgrade) +templates: + - common.yaml +release: + upgrade: true +tests: + - it: should generate correct init container name + set: + image: &image + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + initContainers: + container-name1: + enabled: true + type: init + imageSelector: image + container-name2: + enabled: true + type: upgrade + imageSelector: image + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.initContainers[0] + content: + name: release-name-common-test-upgrade-container-name2 + - documentIndex: *deploymentDoc + isSubset: + path: spec.template.spec.initContainers[1] + content: + name: release-name-common-test-init-container-name1 diff --git a/library/common-test/tests/initContainer/validation_test.yaml b/library/common-test/tests/initContainer/validation_test.yaml new file mode 100644 index 0000000000..f9324e2781 --- /dev/null +++ b/library/common-test/tests/initContainer/validation_test.yaml @@ -0,0 +1,68 @@ +suite: init container data test (upgrade) +templates: + - common.yaml +tests: +# Failures + - it: should fail with empty type on init container + set: + image: + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + initContainers: + container-name1: + enabled: true + type: "" + asserts: + - failedTemplate: + errorMessage: InitContainer - Expected non-empty + + - it: should fail with invalid type on init container + set: + image: + repository: nginx + tag: 1.19.0 + pullPolicy: IfNotPresent + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name1: + enabled: true + primary: true + imageSelector: image + probes: + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + initContainers: + container-name1: + enabled: true + type: invalid + asserts: + - failedTemplate: + errorMessage: InitContainer - Expected to be one of [init, install, upgrade], but got [invalid] diff --git a/library/common-test/tests/job/metadata_test.yaml b/library/common-test/tests/job/metadata_test.yaml new file mode 100644 index 0000000000..84b103f2e2 --- /dev/null +++ b/library/common-test/tests/job/metadata_test.yaml @@ -0,0 +1,84 @@ +suite: job metadata test +templates: + - common.yaml +chart: + appVersion: &appVer v9.9.9 +tests: + - it: should pass with job created with labels and annotations + set: + label1: label1 + label2: global_label2 + annotation1: annotation1 + annotation2: global_annotation2 + global: + labels: + g_label1: global_label1 + g_label2: "{{ .Values.label2 }}" + annotations: + g_annotation1: global_annotation1 + g_annotation2: "{{ .Values.annotation2 }}" + workload: + workload-name: + enabled: true + primary: true + type: Job + labels: + label1: "{{ .Values.label1 }}" + label2: label2 + annotations: + annotation1: "{{ .Values.annotation1 }}" + annotation2: annotation2 + podSpec: + labels: + pod-label1: pod-label1 + pod-label2: "{{ .Values.label2 }}" + annotations: + pod-annotation1: pod-annotation1 + pod-annotation2: "{{ .Values.annotation2 }}" + asserts: + - documentIndex: &jobDoc 0 + isKind: + of: Job + - documentIndex: *jobDoc + equal: + path: metadata.annotations + value: + annotation1: annotation1 + annotation2: annotation2 + g_annotation1: global_annotation1 + g_annotation2: global_annotation2 + - documentIndex: *jobDoc + equal: + path: metadata.labels + value: + app: common-test-1.0.0 + release: RELEASE-NAME + helm-revision: "0" + helm.sh/chart: common-test-1.0.0 + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: *appVer + g_label1: global_label1 + g_label2: global_label2 + label1: label1 + label2: label2 + - documentIndex: *jobDoc + equal: + path: spec.template.metadata.labels + value: + pod.name: workload-name + app: common-test-1.0.0 + release: RELEASE-NAME + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: common-test + app.kubernetes.io/version: v9.9.9 + helm-revision: "0" + helm.sh/chart: common-test-1.0.0 + g_label1: global_label1 + g_label2: global_label2 + pod-label1: pod-label1 + pod-label2: global_label2 + - documentIndex: *jobDoc + matchRegex: + path: spec.template.metadata.annotations.rollme + pattern: '^[0-9a-zA-Z]{5}$' diff --git a/library/common-test/tests/job/spec_test.yaml b/library/common-test/tests/job/spec_test.yaml new file mode 100644 index 0000000000..bef4f4145e --- /dev/null +++ b/library/common-test/tests/job/spec_test.yaml @@ -0,0 +1,54 @@ +suite: job spec test +templates: + - common.yaml +tests: + - it: should pass with workload enabled + set: + workload: + workload-name: + enabled: true + primary: true + type: Job + podSpec: {} + asserts: + - documentIndex: &jobDoc 0 + isKind: + of: Job + - documentIndex: *jobDoc + isAPIVersion: + of: batch/v1 + - documentIndex: *jobDoc + isSubset: + path: spec + content: + backoffLimit: 5 + completionMode: NonIndexed + completions: + parallelism: 1 + ttlSecondsAfterFinished: 120 + + - it: should apply spec correctly + set: + workload: + workload-name: + enabled: true + primary: true + type: Job + backoffLimit: 5 + completionMode: Indexed + completions: 5 + parallelism: 5 + ttlSecondsAfterFinished: 100 + activeDeadlineSeconds: 100 + podSpec: {} + asserts: + - documentIndex: *jobDoc + isSubset: + path: spec + content: + backoffLimit: 5 + completionMode: Indexed + completions: 5 + parallelism: 5 + ttlSecondsAfterFinished: 100 + activeDeadlineSeconds: 100 diff --git a/library/common-test/tests/job/validation_test.yaml b/library/common-test/tests/job/validation_test.yaml new file mode 100644 index 0000000000..2a2f0d0a8b --- /dev/null +++ b/library/common-test/tests/job/validation_test.yaml @@ -0,0 +1,45 @@ +suite: job validation test +templates: + - common.yaml +tests: + - it: should fail with invalid completionMode + set: + workload: + workload-name: + enabled: true + primary: true + type: Job + completionMode: not-a-mode + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Job - Expected to be one of [Indexed, NonIndexed], but got [not-a-mode] + + - it: should fail with completionMode to Indexed and no completions + set: + workload: + workload-name: + enabled: true + primary: true + type: Job + completionMode: Indexed + completions: + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Job - Expected to be set when is set to [Indexed] + + - it: should fail with completionMode to Indexed and no parallelism + set: + workload: + workload-name: + enabled: true + primary: true + type: Job + completionMode: Indexed + completions: 5 + parallelism: + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Job - Expected to be set when is set to [Indexed] diff --git a/library/common-test/tests/persistence/validation_test.yaml b/library/common-test/tests/persistence/validation_test.yaml new file mode 100644 index 0000000000..94063bb197 --- /dev/null +++ b/library/common-test/tests/persistence/validation_test.yaml @@ -0,0 +1,35 @@ +suite: persistence validation test +templates: + - common.yaml +tests: + - it: should fail with pod targetSelector not a map + set: + workload: + main: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + targetSelector: not-a-map + asserts: + - failedTemplate: + errorMessage: Persistence - Expected to be [dict], but got [string] + + - it: should fail with invalid type + set: + workload: + main: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: not-a-type + asserts: + - failedTemplate: + errorMessage: Persistence - Expected to be one of [emptyDir, hostPath, ixVolume, secret, configmap, device], but got [not-a-type] diff --git a/library/common-test/tests/pod/automount_sa_token_test.yaml b/library/common-test/tests/pod/automount_sa_token_test.yaml new file mode 100644 index 0000000000..8511e16f36 --- /dev/null +++ b/library/common-test/tests/pod/automount_sa_token_test.yaml @@ -0,0 +1,75 @@ +suite: pod auto mount sa token test +templates: + - common.yaml +tests: + - it: should pass with automountServiceAccountToken disabled from "global" + set: + podOptions: + automountServiceAccountToken: false + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.automountServiceAccountToken + value: false + + - it: should pass with automountServiceAccountToken enabled from "global" + set: + podOptions: + automountServiceAccountToken: true + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: *deploymentDoc + isKind: + of: Deployment + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.automountServiceAccountToken + value: true + + - it: should pass with disabled automountServiceAccountToken from pod + set: + podOptions: + automountServiceAccountToken: true + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + automountServiceAccountToken: false + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.automountServiceAccountToken + value: false + + - it: should pass with enabled automountServiceAccountToken from pod + set: + podOptions: + automountServiceAccountToken: false + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + automountServiceAccountToken: true + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.automountServiceAccountToken + value: true diff --git a/library/common-test/tests/pod/dns_config_test.yaml b/library/common-test/tests/pod/dns_config_test.yaml new file mode 100644 index 0000000000..798c0d6473 --- /dev/null +++ b/library/common-test/tests/pod/dns_config_test.yaml @@ -0,0 +1,237 @@ +suite: pod dns config test +templates: + - common.yaml +tests: + - it: should pass with empty dnsConfig + set: + podOptions: + dnsConfig: + nameservers: [] + searches: [] + options: [] + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.dnsConfig + + - it: should pass with dnsConfig from "global" + set: + podOptions: + dnsConfig: + nameservers: + - 1.1.1.1 + - 2.2.2.2 + searches: + - example.com + - example.org + options: + - name: ndots + value: "2" + - name: edns0 + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.dnsConfig + value: + nameservers: + - 1.1.1.1 + - 2.2.2.2 + searches: + - example.com + - example.org + options: + - name: ndots + value: "2" + - name: edns0 + + - it: should pass with dnsPolicy from "pod" + set: + podOptions: + dnsConfig: + nameservers: + - 1.1.1.1 + searches: + - example.org + options: + - name: edns0 + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + dnsConfig: + nameservers: + - 1.1.1.1 + - 2.2.2.2 + searches: + - example.com + - example.org + options: + - name: ndots + value: "2" + - name: edns0 + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.dnsConfig + value: + nameservers: + - 1.1.1.1 + - 2.2.2.2 + searches: + - example.com + - example.org + options: + - name: ndots + value: "2" + - name: edns0 + + - it: should pass with dnsPolicy from "pod" with tpl + set: + ns1: 1.1.1.1 + s1: example.com + property: ndots + value: 2 + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + dnsPolicy: None + dnsConfig: + nameservers: + - "{{ .Values.ns1 }}" + - 2.2.2.2 + searches: + - "{{ .Values.s1 }}" + - example.org + options: + - name: "{{ .Values.property }}" + value: "{{ .Values.value }}" + - name: edns0 + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.dnsConfig + value: + nameservers: + - 1.1.1.1 + - 2.2.2.2 + searches: + - example.com + - example.org + options: + - name: ndots + value: "2" + - name: edns0 + + # Failures + - it: should fail with dnsPolicy set to None and no nameservers + set: + podOptions: + dnsPolicy: None + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + dnsConfig: + nameservers: [] + asserts: + - failedTemplate: + errorMessage: Expected non-empty with set to [None]. + + - it: should fail with dnsPolicy set to None and no searches + set: + podOptions: + dnsPolicy: None + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + dnsConfig: + nameservers: + - 1.1.1.1 + searches: [] + asserts: + - failedTemplate: + errorMessage: Expected non-empty with set to [None]. + + - it: should fail with dnsPolicy set to None and no options + set: + podOptions: + dnsPolicy: None + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + dnsConfig: + nameservers: + - 1.1.1.1 + searches: + - example.com + options: [] + asserts: + - failedTemplate: + errorMessage: Expected non-empty with set to [None]. + + - it: should fail with more than 3 nameservers + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + dnsConfig: + nameservers: + - 1.1.1.1 + - 1.1.1.1 + - 1.1.1.1 + - 1.1.1.1 + asserts: + - failedTemplate: + errorMessage: Expected no more than [3] , but got [4] + + - it: should fail with more than 6 Searches + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + dnsConfig: + searches: + - example.com + - example.com + - example.com + - example.com + - example.com + - example.com + - example.com + asserts: + - failedTemplate: + errorMessage: Expected no more than [6] , but got [7] diff --git a/library/common-test/tests/pod/dns_policy_test.yaml b/library/common-test/tests/pod/dns_policy_test.yaml new file mode 100644 index 0000000000..20acfe7b51 --- /dev/null +++ b/library/common-test/tests/pod/dns_policy_test.yaml @@ -0,0 +1,106 @@ +suite: pod dns policy test +templates: + - common.yaml +tests: + - it: should pass with empty dnsPolicy + set: + podOptions: + dnsPolicy: "" + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.dnsPolicy + value: ClusterFirst + + - it: should pass with dnsPolicy from "global" + set: + podOptions: + dnsPolicy: Default + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.dnsPolicy + value: Default + + - it: should pass with dnsPolicy from "pod" + set: + podOptions: + dnsPolicy: None + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + dnsPolicy: ClusterFirst + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.dnsPolicy + value: ClusterFirst + + - it: should pass with dnsPolicy from "pod" with tpl + set: + policy: ClusterFirst + podOptions: + dnsPolicy: None + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + dnsPolicy: "{{ .Values.policy }}" + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.dnsPolicy + value: ClusterFirst + + - it: should pass with hostNetwork enabled + set: + podOptions: + dnsPolicy: None + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + dnsPolicy: ClusterFirst + hostNetwork: true + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.dnsPolicy + value: ClusterFirstWithHostNet + + # Failures + - it: should fail with invalid dnsPolicy + set: + podOptions: + dnsPolicy: Invalid + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Expected to be one of [ClusterFirst, ClusterFirstWithHostNet, Default, None], but got [Invalid] diff --git a/library/common-test/tests/pod/enable_service_links_test.yaml b/library/common-test/tests/pod/enable_service_links_test.yaml new file mode 100644 index 0000000000..3450d4498c --- /dev/null +++ b/library/common-test/tests/pod/enable_service_links_test.yaml @@ -0,0 +1,75 @@ +suite: pod enableServiceLinks test +templates: + - common.yaml +tests: + - it: should pass with enableServiceLinks disabled from "global" + set: + podOptions: + enableServiceLinks: false + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.enableServiceLinks + value: false + + - it: should pass with enableServiceLinks enabled from "global" + set: + podOptions: + enableServiceLinks: true + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: *deploymentDoc + isKind: + of: Deployment + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.enableServiceLinks + value: true + + - it: should pass with disabled enableServiceLinks from pod + set: + podOptions: + enableServiceLinks: true + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + enableServiceLinks: false + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.enableServiceLinks + value: false + + - it: should pass with enabled enableServiceLinks from pod + set: + podOptions: + enableServiceLinks: false + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + enableServiceLinks: true + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.enableServiceLinks + value: true diff --git a/library/common-test/tests/pod/host-aliases_test.yaml b/library/common-test/tests/pod/host-aliases_test.yaml new file mode 100644 index 0000000000..d8d0cf1e97 --- /dev/null +++ b/library/common-test/tests/pod/host-aliases_test.yaml @@ -0,0 +1,126 @@ +suite: pod hostAliases test +templates: + - common.yaml +tests: + - it: should pass with empty hostAliases + set: + podOptions: + hostAliases: "" + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.hostAliases + + - it: should pass with hostAliases from "global" + set: + podOptions: + hostAliases: + - ip: 10.10.10.100 + hostnames: + - myserver.local + - storage.local + - ip: 10.10.10.101 + hostnames: + - myotherserver.local + - backups.local + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.hostAliases + value: + - ip: 10.10.10.100 + hostnames: + - myserver.local + - storage.local + - ip: 10.10.10.101 + hostnames: + - myotherserver.local + - backups.local + + - it: should pass with hostAliases from "pod" + set: + podOptions: + hostAliases: + - ip: 10.10.10.100 + hostnames: + - myserver.local + - storage.local + - ip: 10.10.10.101 + hostnames: + - myotherserver.local + - backups.local + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + hostAliases: + - ip: 10.10.10.200 + hostnames: + - server.local + - mystorage.local + - ip: 10.10.10.201 + hostnames: + - otherserver.local + - mybackups.local + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.hostAliases + value: + - ip: 10.10.10.200 + hostnames: + - server.local + - mystorage.local + - ip: 10.10.10.201 + hostnames: + - otherserver.local + - mybackups.local + + - it: should pass with hostaliases from "pod" with tpl + set: + ip: 10.10.10.200 + host1: server.local + host2: mystorage.local + podOptions: + hostAliases: + - ip: 10.10.10.100 + hostnames: + - myserver.local + - storage.local + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + hostAliases: + - ip: "{{ .Values.ip }}" + hostnames: + - "{{ .Values.host1 }}" + - "{{ .Values.host2 }}" + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.hostAliases + value: + - ip: 10.10.10.200 + hostnames: + - server.local + - mystorage.local diff --git a/library/common-test/tests/pod/host_network_test.yaml b/library/common-test/tests/pod/host_network_test.yaml new file mode 100644 index 0000000000..e4b5147cf2 --- /dev/null +++ b/library/common-test/tests/pod/host_network_test.yaml @@ -0,0 +1,75 @@ +suite: pod hostnetwork test +templates: + - common.yaml +tests: + - it: should pass with hostnetwork disabled from "global" + set: + podOptions: + hostNetwork: false + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.hostNetwork + value: false + + - it: should pass with hostnetwork enabled from "global" + set: + podOptions: + hostNetwork: true + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: *deploymentDoc + isKind: + of: Deployment + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.hostNetwork + value: true + + - it: should pass with disabled hostnetwork from pod + set: + podOptions: + hostNetwork: true + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + hostNetwork: false + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.hostNetwork + value: false + + - it: should pass with enabled hostnetwork from pod + set: + podOptions: + hostNetwork: false + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + hostNetwork: true + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.hostNetwork + value: true diff --git a/library/common-test/tests/pod/hostname_test.yaml b/library/common-test/tests/pod/hostname_test.yaml new file mode 100644 index 0000000000..b740ad98d4 --- /dev/null +++ b/library/common-test/tests/pod/hostname_test.yaml @@ -0,0 +1,56 @@ +suite: pod hostname test +templates: + - common.yaml +tests: + - it: should pass with empty hostname + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.hostname + + - it: should pass with hostname set + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + hostname: some-other-hostname + asserts: + - documentIndex: *deploymentDoc + isKind: + of: Deployment + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.hostname + value: some-other-hostname + + - it: should pass with hostname from "pod" with tpl + set: + host: some-other-hostname + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + hostname: "{{ .Values.host }}" + asserts: + - documentIndex: *deploymentDoc + isKind: + of: Deployment + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.hostname + value: some-other-hostname diff --git a/library/common-test/tests/pod/image_pull_secret_test.yaml b/library/common-test/tests/pod/image_pull_secret_test.yaml new file mode 100644 index 0000000000..c319d58bc8 --- /dev/null +++ b/library/common-test/tests/pod/image_pull_secret_test.yaml @@ -0,0 +1,155 @@ +suite: pod imagePullSecret test +templates: + - common.yaml +tests: + - it: should assign multiple imagePullSecret to primary pod + set: + workload: + workload-name1: + enabled: true + primary: true + type: CronJob + schedule: "* * * * *" + podSpec: {} + workload-name2: + enabled: true + primary: false + type: Deployment + podSpec: {} + imagePullSecret: + pull-secret1: + enabled: true + data: + registry: reg + username: user + password: pass + email: mail + pull-secret2: + enabled: true + data: + registry: reg + username: user + password: pass + email: mail + asserts: + - documentIndex: &cronJobDoc 2 + isKind: + of: CronJob + - documentIndex: *cronJobDoc + equal: + path: metadata.name + value: release-name-common-test + - documentIndex: *cronJobDoc + equal: + path: spec.jobTemplate.spec.template.spec.imagePullSecrets + value: + - name: release-name-common-test-pull-secret1 + - name: release-name-common-test-pull-secret2 + - documentIndex: &otherDeploymentDoc 3 + isKind: + of: Deployment + - documentIndex: *otherDeploymentDoc + isNull: + path: spec.template.spec.imagePullSecrets + + - it: should assign multiple imagePullSecret to all pods + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + workload-name2: + enabled: true + primary: false + type: Deployment + podSpec: {} + imagePullSecret: + pull-secret1: + enabled: true + data: + registry: reg + username: user + password: pass + email: mail + targetSelectAll: true + pull-secret2: + enabled: true + data: + registry: reg + username: user + password: pass + email: mail + targetSelectAll: true + asserts: + - documentIndex: &deploymentDoc 2 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.imagePullSecrets + value: + - name: release-name-common-test-pull-secret1 + - name: release-name-common-test-pull-secret2 + - documentIndex: &otherDeploymentDoc 3 + isKind: + of: Deployment + - documentIndex: *otherDeploymentDoc + equal: + path: spec.template.spec.imagePullSecrets + value: + - name: release-name-common-test-pull-secret1 + - name: release-name-common-test-pull-secret2 + + - it: should assign imagePullSecret to selected pods + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + workload-name2: + enabled: true + primary: false + type: Deployment + podSpec: {} + imagePullSecret: + pull-secret1: + enabled: true + data: + registry: reg + username: user + password: pass + email: mail + targetSelector: + - workload-name1 + - workload-name2 + pull-secret2: + enabled: true + data: + registry: reg + username: user + password: pass + email: mail + targetSelector: + - workload-name1 + asserts: + - documentIndex: *deploymentDoc + isKind: + of: Deployment + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.imagePullSecrets + value: + - name: release-name-common-test-pull-secret1 + - name: release-name-common-test-pull-secret2 + - documentIndex: &otherDeploymentDoc 3 + isKind: + of: Deployment + - documentIndex: *otherDeploymentDoc + equal: + path: spec.template.spec.imagePullSecrets + value: + - name: release-name-common-test-pull-secret1 diff --git a/library/common-test/tests/pod/restart_policy_test.yaml b/library/common-test/tests/pod/restart_policy_test.yaml new file mode 100644 index 0000000000..9597dcc6a4 --- /dev/null +++ b/library/common-test/tests/pod/restart_policy_test.yaml @@ -0,0 +1,137 @@ +suite: pod restart policy test +templates: + - common.yaml +tests: + - it: should pass with restartPolicy empty + set: + podOptions: + restartPolicy: "" + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.restartPolicy + value: Always + + - it: should pass with restartPolicy set from "global" + set: + podOptions: + restartPolicy: Always + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.restartPolicy + value: Always + + - it: should pass with restartPolicy set from "pod" + set: + podOptions: + restartPolicy: Never + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + restartPolicy: Always + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.restartPolicy + value: Always + + - it: should pass with restartPolicy set from "pod" on Jobs + set: + podOptions: + restartPolicy: Never + workload: + workload-name1: + enabled: true + primary: true + type: Job + podSpec: + restartPolicy: OnFailure + asserts: + - documentIndex: &jobDoc 0 + isKind: + of: Job + - documentIndex: *jobDoc + equal: + path: spec.template.spec.restartPolicy + value: OnFailure + + - it: should pass with restartPolicy set from "pod" on Jobs from tpl + set: + policy: OnFailure + podOptions: + restartPolicy: Never + workload: + workload-name1: + enabled: true + primary: true + type: Job + podSpec: + restartPolicy: "{{ .Values.policy }}" + asserts: + - documentIndex: *jobDoc + equal: + path: spec.template.spec.restartPolicy + value: OnFailure + + # Failures + - it: should fail with restartPolicy to Never on Deployment + set: + podOptions: + restartPolicy: Never + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Expected + + - it: should fail with empty fsGroup + set: + securityContext: + pod: + fsGroup: "" + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Pod - Expected non-empty + + - it: should fail with empty fsGroupChangePolicy + set: + securityContext: + pod: + fsGroup: 568 + fsGroupChangePolicy: "" + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Pod - Expected non-empty + + - it: should fail with invalid fsGroupChangePolicy + set: + securityContext: + pod: + fsGroup: 568 + fsGroupChangePolicy: invalid + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Pod - Expected to be one of [Always, OnRootMismatch], but got [invalid] + + - it: should fail with empty name in sysctls + set: + securityContext: + pod: + fsGroup: 568 + fsGroupChangePolicy: OnRootMismatch + sysctls: + - name: "" + value: "some_value" + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Pod - Expected non-empty in + + - it: should fail with empty value in sysctls + set: + securityContext: + pod: + fsGroup: 568 + fsGroupChangePolicy: OnRootMismatch + sysctls: + - name: some_name + value: "" + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Pod - Expected non-empty in diff --git a/library/common-test/tests/pod/service_account_name_test.yaml b/library/common-test/tests/pod/service_account_name_test.yaml new file mode 100644 index 0000000000..35bd993e9c --- /dev/null +++ b/library/common-test/tests/pod/service_account_name_test.yaml @@ -0,0 +1,174 @@ +suite: pod service account name test +templates: + - common.yaml +tests: + - it: should assign serviceAccount to primary pod + set: + workload: + workload-name1: + enabled: true + primary: true + type: CronJob + schedule: "* * * * *" + podSpec: {} + workload-name2: + enabled: true + primary: false + type: Deployment + podSpec: {} + serviceAccount: + sa-name1: + enabled: true + primary: true + asserts: + - documentIndex: &cronJobDoc 1 + isKind: + of: CronJob + - documentIndex: *cronJobDoc + equal: + path: metadata.name + value: release-name-common-test + - documentIndex: *cronJobDoc + equal: + path: spec.jobTemplate.spec.template.spec.serviceAccountName + value: release-name-common-test + - documentIndex: &otherDeploymentDoc 2 + isKind: + of: Deployment + - documentIndex: *otherDeploymentDoc + equal: + path: spec.template.spec.serviceAccountName + value: default + + - it: should assign serviceAccount to all pods + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + workload-name2: + enabled: true + primary: false + type: Deployment + podSpec: {} + serviceAccount: + sa-name1: + enabled: true + primary: true + targetSelectAll: true + asserts: + - documentIndex: &deploymentDoc 1 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.serviceAccountName + value: release-name-common-test + - documentIndex: &otherDeploymentDoc 2 + isKind: + of: Deployment + - documentIndex: *otherDeploymentDoc + equal: + path: spec.template.spec.serviceAccountName + value: release-name-common-test + + - it: should assign serviceAccount to selected pods + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + workload-name2: + enabled: true + primary: false + type: Deployment + podSpec: {} + serviceAccount: + sa-name1: + enabled: true + primary: true + targetSelector: + - workload-name1 + sa-name2: + enabled: true + primary: false + targetSelector: + - workload-name2 + asserts: + - documentIndex: &deploymentDoc 2 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.serviceAccountName + value: release-name-common-test + - documentIndex: &otherDeploymentDoc 3 + isKind: + of: Deployment + - documentIndex: *otherDeploymentDoc + equal: + path: spec.template.spec.serviceAccountName + value: release-name-common-test-sa-name2 + + - it: should assign serviceAccount to selected pods + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + workload-name2: + enabled: true + primary: false + type: Deployment + podSpec: {} + serviceAccount: + sa-name1: + enabled: true + primary: true + targetSelector: + - workload-name1 + - workload-name2 + asserts: + - documentIndex: &deploymentDoc 1 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.serviceAccountName + value: release-name-common-test + - documentIndex: &otherDeploymentDoc 2 + isKind: + of: Deployment + - documentIndex: *otherDeploymentDoc + equal: + path: spec.template.spec.serviceAccountName + value: release-name-common-test + + # Failures + - it: should fail with more than 1 SA assigned to a pod + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + serviceAccount: + sa-name1: + enabled: true + primary: true + targetSelector: + - workload-name1 + sa-name2: + enabled: true + targetSelector: + - workload-name1 + asserts: + - failedTemplate: + errorMessage: Expected at most 1 ServiceAccount to be assigned on a pod [workload-name1]. But [2] were assigned diff --git a/library/common-test/tests/pod/termination_grace_period_test.yaml b/library/common-test/tests/pod/termination_grace_period_test.yaml new file mode 100644 index 0000000000..546a94414e --- /dev/null +++ b/library/common-test/tests/pod/termination_grace_period_test.yaml @@ -0,0 +1,72 @@ +suite: pod termination grace period test +templates: + - common.yaml +tests: + - it: should pass with empty terminationGracePeriodSeconds + set: + podOptions: + terminationGracePeriodSeconds: "" + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.terminationGracePeriodSeconds + + - it: should pass with terminationGracePeriodSeconds from "global" + set: + podOptions: + terminationGracePeriodSeconds: 100 + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.terminationGracePeriodSeconds + value: 100 + + - it: should pass with terminationGracePeriodSeconds from "pod" + set: + podOptions: + terminationGracePeriodSeconds: 100 + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + terminationGracePeriodSeconds: 150 + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.terminationGracePeriodSeconds + value: 150 + + - it: should pass with terminationGracePeriodSeconds from "pod" with tpl + set: + period: 125 + podOptions: + terminationGracePeriodSeconds: 50 + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + terminationGracePeriodSeconds: "{{ .Values.period }}" + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.terminationGracePeriodSeconds + value: 125 diff --git a/library/common-test/tests/pod/tolerations_test.yaml b/library/common-test/tests/pod/tolerations_test.yaml new file mode 100644 index 0000000000..f8e573c4b4 --- /dev/null +++ b/library/common-test/tests/pod/tolerations_test.yaml @@ -0,0 +1,201 @@ +suite: pod tolerations test +templates: + - common.yaml +tests: + - it: should pass with empty tolerations + set: + podOptions: + tolerations: [] + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isNull: + path: spec.template.spec.tolerations + + - it: should pass with tolerations from "global" + set: + podOptions: + tolerations: + - operator: Exists + effect: NoExecute + tolerationSeconds: 3600 + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.tolerations + value: + - operator: Exists + effect: NoExecute + tolerationSeconds: 3600 + + - it: should pass with tolerations from "pod" + set: + podOptions: + tolerations: + - operator: Exists + effect: NoExecute + tolerationSeconds: 3600 + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + tolerations: + - operator: Exists + effect: NoSchedule + tolerationSeconds: 1800 + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.tolerations + value: + - operator: Exists + effect: NoSchedule + tolerationSeconds: 1800 + + - it: should pass with tolerations from "pod" with tpl + set: + op: Exists + effect: NoSchedule + op1: Equal + effect1: PreferNoSchedule + key: key + value: value + podOptions: + tolerations: + - operator: Exists + effect: NoExecute + tolerationSeconds: 3600 + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + tolerations: + - operator: "{{ .Values.op }}" + effect: "{{ .Values.effect }}" + tolerationSeconds: 1800 + - operator: "{{ .Values.op1 }}" + effect: "{{ .Values.effect1 }}" + tolerationSeconds: 1800 + key: "{{ .Values.key }}" + value: "{{ .Values.value }}" + asserts: + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.tolerations + value: + - operator: Exists + effect: NoSchedule + tolerationSeconds: 1800 + - operator: Equal + effect: PreferNoSchedule + tolerationSeconds: 1800 + key: key + value: value + + # Failures + - it: should fail with invalid operator + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + tolerations: + - operator: Invalid + asserts: + - failedTemplate: + errorMessage: Expected to be one of [Exists, Equal] but got [Invalid] + + - it: should fail with empty key and operator set to Equal + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + tolerations: + - operator: Equal + value: value + asserts: + - failedTemplate: + errorMessage: Expected non-empty and with set to [Equal] + + - it: should fail with empty value and operator set to Equal + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + tolerations: + - operator: Equal + key: key + asserts: + - failedTemplate: + errorMessage: Expected non-empty and with set to [Equal] + + - it: should fail with value set and operator set to Exists + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + tolerations: + - operator: Exists + value: value + asserts: + - failedTemplate: + errorMessage: Expected empty with set to [Exists], but got [value] + + - it: should fail with effect set to Invalid + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + tolerations: + - operator: Exists + effect: Invalid + asserts: + - failedTemplate: + errorMessage: Expected to be one of [NoExecute, NoSchedule, PreferNoSchedule], but got [Invalid] + + - it: should fail with tolerationSeconds not a number + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + tolerations: + - operator: Exists + tolerationSeconds: not-a-number + asserts: + - failedTemplate: + errorMessage: Expected to be a number, but got [not-a-number] diff --git a/library/common-test/tests/pod/volume_configmap_test.yaml b/library/common-test/tests/pod/volume_configmap_test.yaml new file mode 100644 index 0000000000..83fd87e547 --- /dev/null +++ b/library/common-test/tests/pod/volume_configmap_test.yaml @@ -0,0 +1,210 @@ +suite: pod configmap volume test +templates: + - common.yaml +tests: + - it: should pass with configmap volume + set: + some_object: some-object-name + some_mode: "0777" + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + conf-vol: + enabled: true + type: configmap + objectName: "{{ .Values.some_object }}" + defaultMode: "{{ .Values.some_mode }}" + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.volumes + content: + name: conf-vol + configMap: + name: release-name-common-test-some-object-name + defaultMode: 0777 + + - it: should pass with configmap volume with items + set: + some_object: some-object-name + some_mode: "0777" + some_key: some-key + some_path: some-path + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + conf-vol: + enabled: true + type: configmap + objectName: "{{ .Values.some_object }}" + defaultMode: "{{ .Values.some_mode }}" + items: + - key: "{{ .Values.some_key }}" + path: "{{ .Values.some_path }}" + - key: some-other-key + path: some-other-path + asserts: + - documentIndex: *deploymentDoc + isKind: + of: Deployment + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.volumes + content: + name: conf-vol + configMap: + name: release-name-common-test-some-object-name + defaultMode: 0777 + items: + - key: some-key + path: some-path + - key: some-other-key + path: some-other-path + + - it: should pass with configmap volume without expanding object name + set: + some_object: some-object-name + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + conf-vol: + enabled: true + type: configmap + objectName: "{{ .Values.some_object }}" + expandObjectName: false + asserts: + - documentIndex: *deploymentDoc + isKind: + of: Deployment + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.volumes + content: + name: conf-vol + configMap: + name: some-object-name + +# Failures + - it: should fail without objectName in configmap + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: configmap + asserts: + - failedTemplate: + errorMessage: Persistence - Expected non-empty on type + + - it: should fail with empty objectName in configmap + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: configmap + objectName: "" + asserts: + - failedTemplate: + errorMessage: Persistence - Expected non-empty on type + + - it: should fail with defaultMode not a string in configmap + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: configmap + objectName: some-object-name + defaultMode: 1234 + asserts: + - failedTemplate: + errorMessage: Persistence - Expected to be [string], but got [float64] + + - it: should fail with defaultMode not in format of "0000"-"0777" in configmap + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: configmap + objectName: some-object-name + defaultMode: "123" + asserts: + - failedTemplate: + errorMessage: Persistence - Expected to have be in format of ["0777"], but got ["123"] + + - it: should fail without key in items in configmap + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: configmap + objectName: some-object-name + items: + - key: "" + path: some-path + asserts: + - failedTemplate: + errorMessage: Persistence - Expected non-empty + + - it: should fail without path in items in configmap + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: configmap + objectName: some-object-name + items: + - key: some-key + path: "" + asserts: + - failedTemplate: + errorMessage: Persistence - Expected non-empty diff --git a/library/common-test/tests/pod/volume_device_test.yaml b/library/common-test/tests/pod/volume_device_test.yaml new file mode 100644 index 0000000000..3e314d258c --- /dev/null +++ b/library/common-test/tests/pod/volume_device_test.yaml @@ -0,0 +1,110 @@ +suite: pod device volume test +templates: + - common.yaml +tests: + - it: should pass with device volume + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + dev-vol: + enabled: true + type: device + hostPath: /dev/something + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.volumes + content: + name: dev-vol + hostPath: + path: /dev/something + + - it: should pass with device volume and type + set: + some_path: /dev/something + some_type: BlockDevice + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + dev-vol: + enabled: true + type: device + hostPath: "{{ .Values.some_path }}" + hostPathType: "{{ .Values.some_type }}" + asserts: + - documentIndex: *deploymentDoc + isKind: + of: Deployment + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.volumes + content: + name: dev-vol + hostPath: + path: /dev/something + type: BlockDevice + +# Failures + - it: should fail without hostPath + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: device + hostPath: "" + asserts: + - failedTemplate: + errorMessage: Persistence - Expected non-empty on type + + - it: should fail with relative hostPath + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: device + hostPath: some-path + asserts: + - failedTemplate: + errorMessage: Persistence - Expected to start with a forward slash [/] on type + + - it: should fail with invalid hostPathType + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: device + hostPath: /some-path + hostPathType: invalid + asserts: + - failedTemplate: + errorMessage: Persistence - Expected to be one of [DirectoryOrCreate, Directory, FileOrCreate, File, Socket, CharDevice, BlockDevice], but got [invalid] 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..b851576d00 --- /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 type diff --git a/library/common-test/tests/pod/volume_hostPath_test.yaml b/library/common-test/tests/pod/volume_hostPath_test.yaml new file mode 100644 index 0000000000..9c60fa0fc7 --- /dev/null +++ b/library/common-test/tests/pod/volume_hostPath_test.yaml @@ -0,0 +1,110 @@ +suite: pod hostPath volume test +templates: + - common.yaml +tests: + - it: should pass with hostPath volume + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + host-vol: + enabled: true + type: hostPath + hostPath: /some-path + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.volumes + content: + name: host-vol + hostPath: + path: /some-path + + - it: should pass with hostPath volume and type + set: + some_path: /some-path + some_type: DirectoryOrCreate + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + host-vol: + enabled: true + type: hostPath + hostPath: "{{ .Values.some_path }}" + hostPathType: "{{ .Values.some_type }}" + asserts: + - documentIndex: *deploymentDoc + isKind: + of: Deployment + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.volumes + content: + name: host-vol + hostPath: + path: /some-path + type: DirectoryOrCreate + +# Failures + - it: should fail without hostPath + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: hostPath + hostPath: "" + asserts: + - failedTemplate: + errorMessage: Persistence - Expected non-empty on type + + - it: should fail with relative hostPath + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: hostPath + hostPath: some-path + asserts: + - failedTemplate: + errorMessage: Persistence - Expected to start with a forward slash [/] on type + + - it: should fail with invalid hostPathType + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: hostPath + hostPath: /some-path + hostPathType: invalid + asserts: + - failedTemplate: + errorMessage: Persistence - Expected to be one of [DirectoryOrCreate, Directory, FileOrCreate, File, Socket, CharDevice, BlockDevice], but got [invalid] diff --git a/library/common-test/tests/pod/volume_ixVolume_test.yaml b/library/common-test/tests/pod/volume_ixVolume_test.yaml new file mode 100644 index 0000000000..60d61abc5f --- /dev/null +++ b/library/common-test/tests/pod/volume_ixVolume_test.yaml @@ -0,0 +1,137 @@ +suite: pod ixVolume volume test +templates: + - common.yaml +tests: + - it: should pass with ixVolume volume + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + ixVolumes: + - /mnt/pool/ix-applications/ix-app + persistence: + ix-vol: + enabled: true + type: ixVolume + datasetName: ix-app + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.volumes + content: + name: ix-vol + hostPath: + path: /mnt/pool/ix-applications/ix-app + + - it: should pass with hostPath volume and type + set: + some_dataset: ix-app + some_type: DirectoryOrCreate + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + ixVolumes: + - /mnt/pool/ix-applications/ix-app + persistence: + ix-vol: + enabled: true + type: ixVolume + datasetName: "{{ .Values.some_dataset }}" + hostPathType: "{{ .Values.some_type }}" + asserts: + - documentIndex: *deploymentDoc + isKind: + of: Deployment + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.volumes + content: + name: ix-vol + hostPath: + path: /mnt/pool/ix-applications/ix-app + type: DirectoryOrCreate + +# Failures + - it: should fail without datasetName + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: ixVolume + datasetname: "" + asserts: + - failedTemplate: + errorMessage: Persistence - Expected non-empty on type + + - it: should fail with empty ixVolumes + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + ixVolumes: [] + persistence: + volume1: + enabled: true + type: ixVolume + datasetName: ix-app + asserts: + - failedTemplate: + errorMessage: Persistence - Expected non-empty in values on type + + - it: should fail with empty ixVolumes + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + ixVolumes: + - /mnt/pool/ix-applications/ix-wrong-app + - /mnt/pool/ix-applications/ix-other-app + persistence: + volume1: + enabled: true + type: ixVolume + datasetName: ix-app + asserts: + - failedTemplate: + errorMessage: Persistence - Expected [ix-app] to exist on list, but list contained [/mnt/pool/ix-applications/ix-wrong-app, /mnt/pool/ix-applications/ix-other-app] on type + + - it: should fail with invalid hostPathType + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + ixVolumes: + - /mnt/pool/ix-applications/ix-app + persistence: + volume1: + enabled: true + type: ixVolume + datasetName: ix-app + hostPathType: invalid + asserts: + - failedTemplate: + errorMessage: Persistence - Expected to be one of [DirectoryOrCreate, Directory, FileOrCreate, File, Socket, CharDevice, BlockDevice], but got [invalid] diff --git a/library/common-test/tests/pod/volume_secret_test.yaml b/library/common-test/tests/pod/volume_secret_test.yaml new file mode 100644 index 0000000000..e3cd995935 --- /dev/null +++ b/library/common-test/tests/pod/volume_secret_test.yaml @@ -0,0 +1,211 @@ +suite: pod secret volume test +templates: + - common.yaml +tests: + - it: should pass with secret volume + set: + some_object: some-object-name + some_mode: "0777" + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + secret-vol: + enabled: true + type: secret + objectName: "{{ .Values.some_object }}" + defaultMode: "{{ .Values.some_mode }}" + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.volumes + content: + name: secret-vol + secret: + secretName: release-name-common-test-some-object-name + defaultMode: 0777 + + - it: should pass with secret volume with items + set: + some_object: some-object-name + some_mode: "0777" + some_key: some-key + some_path: some-path + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + secret-vol: + enabled: true + type: secret + objectName: "{{ .Values.some_object }}" + defaultMode: "{{ .Values.some_mode }}" + items: + - key: "{{ .Values.some_key }}" + path: "{{ .Values.some_path }}" + - key: some-other-key + path: some-other-path + asserts: + - documentIndex: *deploymentDoc + isKind: + of: Deployment + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.volumes + content: + name: secret-vol + secret: + secretName: release-name-common-test-some-object-name + defaultMode: 0777 + items: + - key: some-key + path: some-path + - key: some-other-key + path: some-other-path + + - it: should pass with secret volume without expanding object name + set: + some_object: some-object-name + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + secret-vol: + enabled: true + type: secret + objectName: "{{ .Values.some_object }}" + expandObjectName: false + asserts: + - documentIndex: *deploymentDoc + isKind: + of: Deployment + - documentIndex: *deploymentDoc + contains: + path: spec.template.spec.volumes + content: + name: secret-vol + secret: + secretName: some-object-name + +# Failures + - it: should fail without objectName in secret + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: secret + objectName: "" + asserts: + - failedTemplate: + errorMessage: Persistence - Expected non-empty on type + + - it: should fail with empty objectName in secret + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: secret + objectName: "" + asserts: + - failedTemplate: + errorMessage: Persistence - Expected non-empty on type + + - it: should fail with defaultMode not a string in secret + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: secret + objectName: some-object-name + defaultMode: 1234 + asserts: + - failedTemplate: + errorMessage: Persistence - Expected to be [string], but got [float64] + + - it: should fail with defaultMode not in format of "0000"-"0777" in secret + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: secret + objectName: some-object-name + defaultMode: "123" + asserts: + - failedTemplate: + errorMessage: Persistence - Expected to have be in format of ["0777"], but got ["123"] + + - it: should fail without key in items in secret + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: secret + objectName: some-object-name + items: + - key: "" + path: some-path + asserts: + - failedTemplate: + errorMessage: Persistence - Expected non-empty + + - it: should fail without path in items in secret + set: + workload: + some-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + persistence: + volume1: + enabled: true + type: secret + objectName: some-object-name + items: + - key: some-key + path: "" + asserts: + - failedTemplate: + errorMessage: Persistence - Expected non-empty diff --git a/library/common-test/tests/rbac/data_test.yaml b/library/common-test/tests/rbac/data_test.yaml new file mode 100644 index 0000000000..644673ee03 --- /dev/null +++ b/library/common-test/tests/rbac/data_test.yaml @@ -0,0 +1,250 @@ +suite: rbac data test +templates: + - common.yaml +tests: + - it: should pass with rules and subjects added with tpl and primary rbac/sa + set: + some_verb: list + some_group: apps + some_resource: deployments + some_kind: some-kind + some_name: some-name + some_api_group: rbac.authorization.k8s.io + serviceAccount: + my-sa: + enabled: true + primary: true + my-other-sa: + enabled: true + primary: false + rbac: + my-rbac: + enabled: true + primary: true + rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - apiGroups: + - "{{ .Values.some_group }}" + resources: + - "{{ .Values.some_resource }}" + verbs: + - "{{ .Values.some_verb }}" + subjects: + - kind: a-kind + name: a-name + apiGroup: rbac.authorization.k8s.io + - kind: "{{ .Values.some_kind }}" + name: "{{ .Values.some_name }}" + apiGroup: "{{ .Values.some_api_group }}" + + asserts: + - documentIndex: &roleDoc 2 + isKind: + of: Role + - documentIndex: *roleDoc + equal: + path: metadata.name + value: release-name-common-test + - documentIndex: *roleDoc + equal: + path: rules + value: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - apiGroups: + - apps + resources: + - deployments + verbs: + - list + - documentIndex: &roleBinding 3 + isKind: + of: RoleBinding + - documentIndex: *roleBinding + equal: + path: metadata.name + value: release-name-common-test + - documentIndex: *roleBinding + equal: + path: subjects + value: + - kind: ServiceAccount + name: release-name-common-test + namespace: NAMESPACE + - kind: a-kind + name: a-name + apiGroup: rbac.authorization.k8s.io + - kind: some-kind + name: some-name + apiGroup: rbac.authorization.k8s.io + + - it: should pass with rules and subjects added with tpl and allSA on clusterWide + set: + some_verb: list + some_group: apps + some_resource: deployments + some_kind: some-kind + some_name: some-name + some_api_group: rbac.authorization.k8s.io + serviceAccount: + my-sa: + enabled: true + primary: true + my-other-sa: + enabled: true + primary: false + rbac: + z-rbac: + enabled: true + primary: true + rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + my-rbac2: + enabled: true + clusterWide: true + allServiceAccounts: true + rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - apiGroups: + - "{{ .Values.some_group }}" + resources: + - "{{ .Values.some_resource }}" + verbs: + - "{{ .Values.some_verb }}" + subjects: + - kind: a-kind + name: a-name + apiGroup: rbac.authorization.k8s.io + - kind: "{{ .Values.some_kind }}" + name: "{{ .Values.some_name }}" + apiGroup: "{{ .Values.some_api_group }}" + asserts: + - documentIndex: &clusterRoleDoc 2 + isKind: + of: ClusterRole + - documentIndex: *clusterRoleDoc + equal: + path: metadata.name + value: release-name-common-test-my-rbac2 + - documentIndex: *clusterRoleDoc + equal: + path: rules + value: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - apiGroups: + - apps + resources: + - deployments + verbs: + - list + - documentIndex: &clusterRoleBinding 3 + isKind: + of: ClusterRoleBinding + - documentIndex: *clusterRoleBinding + equal: + path: metadata.name + value: release-name-common-test-my-rbac2 + - documentIndex: *clusterRoleBinding + equal: + path: subjects + value: + - kind: ServiceAccount + name: release-name-common-test-my-other-sa + namespace: NAMESPACE + - kind: ServiceAccount + name: release-name-common-test + namespace: NAMESPACE + - apiGroup: rbac.authorization.k8s.io + kind: a-kind + name: a-name + - apiGroup: rbac.authorization.k8s.io + kind: some-kind + name: some-name + + - it: should pass with serviceAccount selector + set: + serviceAccount: + my-sa: + enabled: true + primary: true + my-other-sa: + enabled: true + primary: false + rbac: + z-rbac: + enabled: true + primary: true + rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + my-rbac3: + enabled: true + serviceAccounts: + - my-other-sa + rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + asserts: + - documentIndex: &roleDoc 2 + isKind: + of: Role + - documentIndex: *roleDoc + equal: + path: metadata.name + value: release-name-common-test-my-rbac3 + - documentIndex: *roleDoc + equal: + path: rules + value: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - documentIndex: &roleBinding 3 + isKind: + of: RoleBinding + - documentIndex: *roleBinding + equal: + path: metadata.name + value: release-name-common-test-my-rbac3 + - documentIndex: *roleBinding + equal: + path: subjects + value: + - kind: ServiceAccount + name: release-name-common-test-my-other-sa + namespace: NAMESPACE diff --git a/library/common-test/tests/rbac/metadata_test.yaml b/library/common-test/tests/rbac/metadata_test.yaml new file mode 100644 index 0000000000..04bdd573dd --- /dev/null +++ b/library/common-test/tests/rbac/metadata_test.yaml @@ -0,0 +1,163 @@ +suite: rbac metadata test +templates: + - common.yaml +chart: + appVersion: &appVer v9.9.9 +tests: + - it: should pass with rbac created with labels and annotations + set: + label1: label1 + label2: global_label2 + annotation1: annotation1 + annotation2: global_annotation2 + global: + labels: + g_label1: global_label1 + g_label2: "{{ .Values.label2 }}" + annotations: + g_annotation1: global_annotation1 + g_annotation2: "{{ .Values.annotation2 }}" + serviceAccount: + my-sa1: + enabled: true + primary: true + rbac: + my-rbac1: + enabled: true + primary: true + labels: + label1: "{{ .Values.label1 }}" + label2: label2 + annotations: + annotation1: "{{ .Values.annotation1 }}" + annotation2: annotation2 + rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch + my-rbac2: + enabled: true + primary: false + clusterWide: true + allServiceAccounts: true + labels: + label1: "{{ .Values.label1 }}" + label2: label2 + annotations: + annotation1: "{{ .Values.annotation1 }}" + annotation2: annotation2 + rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch + asserts: + - documentIndex: &roleDoc 1 + isKind: + of: Role + - documentIndex: *roleDoc + equal: + path: metadata.annotations + value: + annotation1: annotation1 + annotation2: annotation2 + g_annotation1: global_annotation1 + g_annotation2: global_annotation2 + - documentIndex: *roleDoc + equal: + path: metadata.labels + value: + app: common-test-1.0.0 + release: RELEASE-NAME + helm-revision: "0" + helm.sh/chart: common-test-1.0.0 + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: *appVer + g_label1: global_label1 + g_label2: global_label2 + label1: label1 + label2: label2 + - documentIndex: &roleBindingDoc 2 + isKind: + of: RoleBinding + - documentIndex: *roleBindingDoc + equal: + path: metadata.annotations + value: + annotation1: annotation1 + annotation2: annotation2 + g_annotation1: global_annotation1 + g_annotation2: global_annotation2 + - documentIndex: *roleBindingDoc + equal: + path: metadata.labels + value: + app: common-test-1.0.0 + release: RELEASE-NAME + helm-revision: "0" + helm.sh/chart: common-test-1.0.0 + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: *appVer + g_label1: global_label1 + g_label2: global_label2 + label1: label1 + label2: label2 + - documentIndex: &clusterRoleDoc 3 + isKind: + of: ClusterRole + - documentIndex: *clusterRoleDoc + equal: + path: metadata.annotations + value: + annotation1: annotation1 + annotation2: annotation2 + g_annotation1: global_annotation1 + g_annotation2: global_annotation2 + - documentIndex: *clusterRoleDoc + equal: + path: metadata.labels + value: + app: common-test-1.0.0 + release: RELEASE-NAME + helm-revision: "0" + helm.sh/chart: common-test-1.0.0 + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: *appVer + g_label1: global_label1 + g_label2: global_label2 + label1: label1 + label2: label2 + - documentIndex: &clusterRoleBindingDoc 4 + isKind: + of: ClusterRoleBinding + - documentIndex: *clusterRoleBindingDoc + equal: + path: metadata.annotations + value: + annotation1: annotation1 + annotation2: annotation2 + g_annotation1: global_annotation1 + g_annotation2: global_annotation2 + - documentIndex: *clusterRoleBindingDoc + equal: + path: metadata.labels + value: + app: common-test-1.0.0 + release: RELEASE-NAME + helm-revision: "0" + helm.sh/chart: common-test-1.0.0 + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: *appVer + g_label1: global_label1 + g_label2: global_label2 + label1: label1 + label2: label2 diff --git a/library/common-test/tests/rbac/name_test.yaml b/library/common-test/tests/rbac/name_test.yaml new file mode 100644 index 0000000000..c0c9ab7b8a --- /dev/null +++ b/library/common-test/tests/rbac/name_test.yaml @@ -0,0 +1,105 @@ +suite: rbac name test +templates: + - common.yaml +tests: + - it: should generate correct name + set: + serviceAccount: + my-sa: + enabled: true + primary: true + rbac: + my-rbac: + enabled: true + primary: true + rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + my-rbac2: + enabled: true + clusterWide: true + allServiceAccounts: true + rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + my-rbac3: + enabled: true + allServiceAccounts: true + rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + asserts: + - documentIndex: &roleDoc 1 + isKind: + of: Role + - documentIndex: *roleDoc + isAPIVersion: + of: rbac.authorization.k8s.io/v1 + - documentIndex: *roleDoc + equal: + path: metadata.name + value: release-name-common-test + - documentIndex: &roleBindingDoc 2 + isKind: + of: RoleBinding + - documentIndex: *roleBindingDoc + isAPIVersion: + of: rbac.authorization.k8s.io/v1 + - documentIndex: *roleBindingDoc + equal: + path: metadata.name + value: release-name-common-test + + - documentIndex: &clusterRoleDoc 3 + isKind: + of: ClusterRole + - documentIndex: *clusterRoleDoc + isAPIVersion: + of: rbac.authorization.k8s.io/v1 + - documentIndex: *clusterRoleDoc + equal: + path: metadata.name + value: release-name-common-test-my-rbac2 + - documentIndex: &clusterRoleBindingDoc 4 + isKind: + of: ClusterRoleBinding + - documentIndex: *clusterRoleBindingDoc + isAPIVersion: + of: rbac.authorization.k8s.io/v1 + - documentIndex: *clusterRoleBindingDoc + equal: + path: metadata.name + value: release-name-common-test-my-rbac2 + + - documentIndex: &otherRoleDoc 5 + isKind: + of: Role + - documentIndex: *otherRoleDoc + isAPIVersion: + of: rbac.authorization.k8s.io/v1 + - documentIndex: *otherRoleDoc + equal: + path: metadata.name + value: release-name-common-test-my-rbac3 + - documentIndex: &otherRoleBindingDoc 6 + isKind: + of: RoleBinding + - documentIndex: *otherRoleBindingDoc + isAPIVersion: + of: rbac.authorization.k8s.io/v1 + - documentIndex: *otherRoleBindingDoc + equal: + path: metadata.name + value: release-name-common-test-my-rbac3 diff --git a/library/common-test/tests/rbac/validation_test.yaml b/library/common-test/tests/rbac/validation_test.yaml new file mode 100644 index 0000000000..e9bb71a51a --- /dev/null +++ b/library/common-test/tests/rbac/validation_test.yaml @@ -0,0 +1,250 @@ +suite: rbac validation test +templates: + - common.yaml +tests: + - it: should fail with name longer than 63 characters + set: + rbac: + zmy-rbac: + enabled: true + primary: true + my-rbac-has-super-long-name-that-is-longer-than-63-characters-too-bad: + enabled: true + primary: false + asserts: + - failedTemplate: + errorMessage: Name [release-name-common-test-my-rbac-has-super-long-name-that-is-longer-than-63-characters-too-bad] is not valid. Must start and end with an alphanumeric lowercase character. It can contain '-'. And must be at most 63 characters. + + - it: should fail with name starting with underscore + set: + rbac: + my-rbac: + enabled: true + primary: true + rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + _my-rbac2: + enabled: true + primary: false + asserts: + - failedTemplate: + errorMessage: Name [release-name-common-test-_my-rbac2] is not valid. Must start and end with an alphanumeric lowercase character. It can contain '-'. And must be at most 63 characters. + + - it: should fail with labels not a dict + set: + rbac: + my-rbac: + enabled: true + primary: true + labels: "not a dict" + asserts: + - failedTemplate: + errorMessage: RBAC - Expected to be a dictionary, but got [string] + + - it: should fail with annotations not a dict + set: + rbac: + my-rbac: + enabled: true + primary: true + annotations: "not a dict" + asserts: + - failedTemplate: + errorMessage: RBAC - Expected to be a dictionary, but got [string] + + - it: should fail with more than 1 primary rbac + set: + rbac: + my-rbac: + enabled: true + primary: true + my-rbac2: + enabled: true + primary: true + asserts: + - failedTemplate: + errorMessage: RBAC - Only one rbac can be primary + + - it: should fail without any primary on enabled rbac + set: + rbac: + my-rbac: + enabled: true + primary: false + my-rbac2: + enabled: true + primary: false + asserts: + - failedTemplate: + errorMessage: RBAC - At least one enabled rbac must be primary + + - it: should fail without rules in rbac + set: + rbac: + my-rbac: + enabled: true + primary: true + asserts: + - failedTemplate: + errorMessage: RBAC - Expected non-empty + + - it: should fail without apiGroups in rules in rbac + set: + rbac: + my-rbac: + enabled: true + primary: true + rules: + - resources: + - pods + verbs: + - get + asserts: + - failedTemplate: + errorMessage: RBAC - Expected non-empty + + - it: should fail without resources in rules in rbac + set: + rbac: + my-rbac: + enabled: true + primary: true + rules: + - apiGroups: + - "" + verbs: + - get + asserts: + - failedTemplate: + errorMessage: RBAC - Expected non-empty + + - it: should fail without verbs in rules in rbac + set: + rbac: + my-rbac: + enabled: true + primary: true + rules: + - apiGroups: + - "" + resources: + - pods + asserts: + - failedTemplate: + errorMessage: RBAC - Expected non-empty + + - it: should fail with empty entry in resources in rules in rbac + set: + rbac: + my-rbac: + enabled: true + primary: true + rules: + - apiGroups: + - "" + resources: + - pods + - "" + verbs: + - get + asserts: + - failedTemplate: + errorMessage: RBAC - Expected non-empty entry in + + - it: should fail with empty entry in verbs in rules in rbac + set: + rbac: + my-rbac: + enabled: true + primary: true + rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - "" + asserts: + - failedTemplate: + errorMessage: RBAC - Expected non-empty entry in + + - it: should fail with empty kind in subjects in rbac + set: + serviceAccount: + my-service-account: + enabled: true + primary: true + rbac: + my-rbac: + enabled: true + primary: true + rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + subjects: + - kind: "" + name: my-name + apiGroup: my-apiGroup + asserts: + - failedTemplate: + errorMessage: RBAC - Expected non-empty + + - it: should fail with empty name in subjects in rbac + set: + serviceAccount: + my-service-account: + enabled: true + primary: true + rbac: + my-rbac: + enabled: true + primary: true + rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + subjects: + - kind: my-kind + name: "" + apiGroup: my-apiGroup + asserts: + - failedTemplate: + errorMessage: RBAC - Expected non-empty + + - it: should fail with empty apiGroup in subjects in rbac + set: + serviceAccount: + my-service-account: + enabled: true + primary: true + rbac: + my-rbac: + enabled: true + primary: true + rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + subjects: + - kind: my-kind + name: my-name + apiGroup: "" + asserts: + - failedTemplate: + errorMessage: RBAC - Expected non-empty diff --git a/library/common-test/tests/secret/data_test.yaml b/library/common-test/tests/secret/data_test.yaml new file mode 100644 index 0000000000..932d93fd9f --- /dev/null +++ b/library/common-test/tests/secret/data_test.yaml @@ -0,0 +1,111 @@ +suite: secret data test +templates: + - common.yaml +tests: + - it: should pass with key-value data + set: + secret: + my-secret1: + enabled: true + data: + foo: bar + asserts: + - documentIndex: &secretDoc 0 + isKind: + of: Secret + - documentIndex: *secretDoc + equal: + path: stringData + value: + foo: bar + - documentIndex: *secretDoc + equal: + path: type + value: Opaque + + - it: should pass with custom secret type + set: + secret: + my-secret1: + enabled: true + type: SomeCustomType + data: + foo: bar + asserts: + - documentIndex: *secretDoc + equal: + path: type + value: SomeCustomType + + + - it: should pass with key-value data from tpl + set: + data: bar + secret: + my-secret1: + enabled: true + data: + foo: "{{ .Values.data }}" + asserts: + - documentIndex: *secretDoc + equal: + path: stringData + value: + foo: bar + + - it: should pass with scalar data + set: + secret: + my-secret1: + enabled: true + data: + foo: | + some multi line + string text + asserts: + - documentIndex: *secretDoc + equal: + path: stringData + value: + foo: | + some multi line + string text + + - it: should pass with scalar data with tpl + set: + data: Some other text + secret: + my-secret: + enabled: true + data: + foo: | + file start + {{ .Values.data }} + asserts: + - documentIndex: *secretDoc + equal: + path: stringData + value: + foo: | + file start + Some other text + + - it: should pass with scalar data from tpl + set: + data: | + Some other text + some_text + secret: + my-secret: + enabled: true + data: + foo: | + {{- .Values.data | nindent 2 }} + asserts: + - documentIndex: *secretDoc + equal: + path: stringData + value: + foo: | + Some other text + some_text diff --git a/library/common-test/tests/secret/metadata_test.yaml b/library/common-test/tests/secret/metadata_test.yaml new file mode 100644 index 0000000000..9a6e23f18d --- /dev/null +++ b/library/common-test/tests/secret/metadata_test.yaml @@ -0,0 +1,56 @@ +suite: secret metadata test +templates: + - common.yaml +chart: + appVersion: &appVer v9.9.9 +tests: + - it: should pass with secret created with labels and annotations + set: + label1: label1 + label2: global_label2 + annotation1: annotation1 + annotation2: global_annotation2 + global: + labels: + g_label1: global_label1 + g_label2: "{{ .Values.label2 }}" + annotations: + g_annotation1: global_annotation1 + g_annotation2: "{{ .Values.annotation2 }}" + secret: + my-secret1: + enabled: true + labels: + label1: "{{ .Values.label1 }}" + label2: label2 + annotations: + annotation1: "{{ .Values.annotation1 }}" + annotation2: annotation2 + data: + foo: bar + asserts: + - documentIndex: &secretDoc 0 + isKind: + of: Secret + - documentIndex: *secretDoc + equal: + path: metadata.annotations + value: + annotation1: annotation1 + annotation2: annotation2 + g_annotation1: global_annotation1 + g_annotation2: global_annotation2 + - documentIndex: *secretDoc + equal: + path: metadata.labels + value: + app: common-test-1.0.0 + release: RELEASE-NAME + helm-revision: "0" + helm.sh/chart: common-test-1.0.0 + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: *appVer + g_label1: global_label1 + g_label2: global_label2 + label1: label1 + label2: label2 diff --git a/library/common-test/tests/secret/name_test.yaml b/library/common-test/tests/secret/name_test.yaml new file mode 100644 index 0000000000..2c56e5cf0e --- /dev/null +++ b/library/common-test/tests/secret/name_test.yaml @@ -0,0 +1,36 @@ +suite: secret name test +templates: + - common.yaml +tests: + - it: should generate correct name + set: + secret: + my-secret1: + enabled: true + data: + foo: bar + my-secret2: + enabled: true + data: + foo: bar + asserts: + - documentIndex: &secretDoc 0 + isKind: + of: Secret + - documentIndex: *secretDoc + isAPIVersion: + of: v1 + - documentIndex: *secretDoc + equal: + path: metadata.name + value: release-name-common-test-my-secret1 + - documentIndex: &otherSecretDoc 1 + isKind: + of: Secret + - documentIndex: *otherSecretDoc + isAPIVersion: + of: v1 + - documentIndex: *otherSecretDoc + equal: + path: metadata.name + value: release-name-common-test-my-secret2 diff --git a/library/common-test/tests/secret/validation_test.yaml b/library/common-test/tests/secret/validation_test.yaml new file mode 100644 index 0000000000..e91fc68e23 --- /dev/null +++ b/library/common-test/tests/secret/validation_test.yaml @@ -0,0 +1,81 @@ +suite: secret validation test +templates: + - common.yaml +tests: + - it: should fail with name longer than 63 characters + set: + secret: + my-secret-super-long-name-that-is-longer-than-63-characters: + enabled: true + data: + foo: bar + asserts: + - failedTemplate: + errorMessage: Name [release-name-common-test-my-secret-super-long-name-that-is-longer-than-63-characters] is not valid. Must start and end with an alphanumeric lowercase character. It can contain '-'. And must be at most 63 characters. + + - it: should fail with name starting with underscore + set: + secret: + _my-secret: + enabled: true + data: + foo: bar + asserts: + - failedTemplate: + errorMessage: Name [release-name-common-test-_my-secret] is not valid. Must start and end with an alphanumeric lowercase character. It can contain '-'. And must be at most 63 characters. + + - it: should fail with labels not a dict + set: + secret: + my-secret: + enabled: true + labels: "not a dict" + data: + foo: bar + asserts: + - failedTemplate: + errorMessage: Secret - Expected to be a dictionary, but got [string] + + - it: should fail with annotations not a dict + set: + secret: + my-secret: + enabled: true + annotations: "not a dict" + data: + foo: bar + asserts: + - failedTemplate: + errorMessage: Secret - Expected to be a dictionary, but got [string] + + - it: should fail with data not a dict + set: + secret: + my-secret: + enabled: true + data: "not a dict" + asserts: + - failedTemplate: + errorMessage: Secret - Expected to be a dictionary, but got [string] + + - it: should fail with empty data + set: + secret: + my-secret: + enabled: true + data: {} + asserts: + - failedTemplate: + errorMessage: Secret - Expected non-empty + + - it: should fail with empty type key + set: + secret: + my-secret: + enabled: true + type: "" + data: + foo: bar + asserts: + - failedTemplate: + errorMessage: Secret - Found key, but it's empty diff --git a/library/common-test/tests/service/cluster_ip_test.yaml b/library/common-test/tests/service/cluster_ip_test.yaml new file mode 100644 index 0000000000..423de7398c --- /dev/null +++ b/library/common-test/tests/service/cluster_ip_test.yaml @@ -0,0 +1,126 @@ +suite: service clusterIP test +templates: + - common.yaml +tests: + - it: should pass with type ClusterIP + set: + service: + my-service: + enabled: true + primary: true + type: ClusterIP + ports: + port-name: + enabled: true + primary: true + port: 12345 + workload: + my-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: &serviceDoc 1 + isKind: + of: Service + - documentIndex: *serviceDoc + isAPIVersion: + of: v1 + - documentIndex: *serviceDoc + equal: + path: metadata.name + value: release-name-common-test + - documentIndex: *serviceDoc + equal: + path: spec + value: + type: ClusterIP + publishNotReadyAddresses: false + ports: + - name: port-name + port: 12345 + protocol: TCP + targetPort: 12345 + selector: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/name: common-test + pod.name: my-workload + + - it: should pass with type ClusterIP and available options set + set: + some_policy: PreferDualStack + some_family: IPv6 + some_ip: 172.16.20.35 + some_other_ip: 10.200.34.53 + some_affinity: ClientIP + some_timeout: 100 + some_port: 12344 + some_target_port: 12346 + some_protocol: http + service: + my-service: + enabled: true + primary: true + type: ClusterIP + clusterIP: "{{ .Values.some_ip }}" + publishNotReadyAddresses: true + ipFamilyPolicy: "{{ .Values.some_policy }}" + ipFamilies: + - IPv4 + - "{{ .Values.some_family }}" + externalIPs: + - "{{ .Values.some_other_ip }}" + - 10.200.34.54 + sessionAffinity: "{{ .Values.some_affinity }}" + sessionAffinityConfig: + clientIP: + timeoutSeconds: "{{ .Values.some_timeout }}" + ports: + port-name: + enabled: true + primary: true + port: 12345 + port-name2: + enabled: true + port: "{{ .Values.some_port }}" + targetPort: "{{ .Values.some_target_port }}" + protocol: "{{ .Values.some_protocol }}" + workload: + my-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: *serviceDoc + equal: + path: spec + value: + type: ClusterIP + clusterIP: 172.16.20.35 + ipFamilyPolicy: PreferDualStack + publishNotReadyAddresses: true + ipFamilies: + - IPv4 + - IPv6 + externalIPs: + - 10.200.34.53 + - 10.200.34.54 + sessionAffinity: ClientIP + sessionAffinityConfig: + clientIP: + timeoutSeconds: 100 + ports: + - name: port-name + port: 12345 + protocol: TCP + targetPort: 12345 + - name: port-name2 + port: 12344 + protocol: TCP + targetPort: 12346 + selector: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/name: common-test + pod.name: my-workload diff --git a/library/common-test/tests/service/metadata_test.yaml b/library/common-test/tests/service/metadata_test.yaml new file mode 100644 index 0000000000..0854c5fc1b --- /dev/null +++ b/library/common-test/tests/service/metadata_test.yaml @@ -0,0 +1,158 @@ +suite: service metadata test +templates: + - common.yaml +chart: + appVersion: &appVer v9.9.9 +tests: + - it: should pass with service created with labels and annotations + set: + label1: label1 + label2: global_label2 + annotation1: annotation1 + annotation2: global_annotation2 + global: + labels: + g_label1: global_label1 + g_label2: "{{ .Values.label2 }}" + annotations: + g_annotation1: global_annotation1 + g_annotation2: "{{ .Values.annotation2 }}" + service: + my-service1: + enabled: true + primary: true + labels: + label1: "{{ .Values.label1 }}" + label2: label2 + annotations: + annotation1: "{{ .Values.annotation1 }}" + annotation2: annotation2 + ports: + port-name: + enabled: true + primary: true + port: 12345 + my-service2: + enabled: true + primary: false + ports: + port-name: + enabled: true + primary: true + port: 1234 + workload: + my-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: &serviceDoc 1 + isKind: + of: Service + - documentIndex: *serviceDoc + equal: + path: metadata.annotations + value: + annotation1: annotation1 + annotation2: annotation2 + g_annotation1: global_annotation1 + g_annotation2: global_annotation2 + - documentIndex: *serviceDoc + equal: + path: metadata.labels + value: + app: common-test-1.0.0 + release: RELEASE-NAME + helm-revision: "0" + helm.sh/chart: common-test-1.0.0 + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: *appVer + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/name: common-test + service.name: release-name-common-test + g_label1: global_label1 + g_label2: global_label2 + label1: label1 + label2: label2 + - documentIndex: &otherServiceDoc 2 + isKind: + of: Service + - documentIndex: *otherServiceDoc + equal: + path: metadata.labels + value: + service.name: release-name-common-test-my-service2 + app: common-test-1.0.0 + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: common-test + app.kubernetes.io/version: *appVer + g_label1: global_label1 + g_label2: global_label2 + helm-revision: "0" + helm.sh/chart: common-test-1.0.0 + release: RELEASE-NAME + + - it: should pass with correct selector with targetSelector + set: + service: + my-service1: + enabled: true + primary: true + targetSelector: my-workload + ports: + port-name: + enabled: true + primary: true + port: 12345 + workload: + my-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: *serviceDoc + isKind: + of: Service + - documentIndex: *serviceDoc + equal: + path: spec.selector + value: + pod.name: my-workload + app.kubernetes.io/name: common-test + app.kubernetes.io/instance: RELEASE-NAME + + - it: should pass with correct selector without targetSelector + set: + service: + my-service1: + enabled: true + primary: true + ports: + port-name: + enabled: true + primary: true + port: 12345 + workload: + my-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + my-workload2: + enabled: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: &serviceDoc 2 + isKind: + of: Service + - documentIndex: *serviceDoc + equal: + path: spec.selector + value: + pod.name: my-workload + app.kubernetes.io/name: common-test + app.kubernetes.io/instance: RELEASE-NAME diff --git a/library/common-test/tests/service/names_test.yaml b/library/common-test/tests/service/names_test.yaml new file mode 100644 index 0000000000..878d40d107 --- /dev/null +++ b/library/common-test/tests/service/names_test.yaml @@ -0,0 +1,49 @@ +suite: service name test +templates: + - common.yaml +tests: + - it: should generate correct name + set: + service: + my-service1: + enabled: true + primary: true + ports: + port-name: + enabled: true + primary: true + port: 12344 + my-service2: + enabled: true + ports: + port-name: + enabled: true + primary: true + port: 12345 + workload: + my-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: &serviceDoc 1 + isKind: + of: Service + - documentIndex: *serviceDoc + isAPIVersion: + of: v1 + - documentIndex: *serviceDoc + equal: + path: metadata.name + value: release-name-common-test + - documentIndex: &otherServiceDoc 2 + isKind: + of: Service + - documentIndex: *otherServiceDoc + isAPIVersion: + of: v1 + - documentIndex: *otherServiceDoc + equal: + path: metadata.name + value: release-name-common-test-my-service2 diff --git a/library/common-test/tests/service/node_port_test.yaml b/library/common-test/tests/service/node_port_test.yaml new file mode 100644 index 0000000000..a0377ac7ae --- /dev/null +++ b/library/common-test/tests/service/node_port_test.yaml @@ -0,0 +1,135 @@ +suite: service nodePort test +templates: + - common.yaml +tests: + - it: should pass with type nodePort + set: + service: + my-service: + enabled: true + primary: true + type: NodePort + ports: + port-name: + enabled: true + primary: true + port: 12345 + nodePort: 30000 + workload: + my-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: &serviceDoc 1 + isKind: + of: Service + - documentIndex: *serviceDoc + isAPIVersion: + of: v1 + - documentIndex: *serviceDoc + equal: + path: metadata.name + value: release-name-common-test + - documentIndex: *serviceDoc + equal: + path: spec + value: + type: NodePort + publishNotReadyAddresses: false + ports: + - name: port-name + port: 12345 + protocol: TCP + targetPort: 12345 + nodePort: 30000 + selector: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/name: common-test + pod.name: my-workload + + - it: should pass with type NodePort and available options set + set: + some_ip: 172.16.20.35 + some_family: IPv6 + some_policy: Local + some_other_ip: 10.200.34.53 + some_affinity: ClientIP + some_timeout: 100 + some_port: 12344 + some_target_port: 12346 + some_protocol: http + some_nodePort: 30001 + service: + my-service: + enabled: true + primary: true + type: NodePort + clusterIP: "{{ .Values.some_ip }}" + externalTrafficPolicy: "{{ .Values.some_policy }}" + publishNotReadyAddresses: true + ipFamilyPolicy: PreferDualStack + ipFamilies: + - IPv4 + - "{{ .Values.some_family }}" + externalIPs: + - "{{ .Values.some_other_ip }}" + - 10.200.34.54 + sessionAffinity: "{{ .Values.some_affinity }}" + sessionAffinityConfig: + clientIP: + timeoutSeconds: "{{ .Values.some_timeout }}" + ports: + port-name: + enabled: true + primary: true + port: 12345 + nodePort: 30000 + port-name2: + enabled: true + port: "{{ .Values.some_port }}" + targetPort: "{{ .Values.some_target_port }}" + protocol: "{{ .Values.some_protocol }}" + nodePort: "{{ .Values.some_nodePort }}" + workload: + my-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: *serviceDoc + equal: + path: spec + value: + type: NodePort + clusterIP: 172.16.20.35 + externalTrafficPolicy: Local + ipFamilyPolicy: PreferDualStack + publishNotReadyAddresses: true + ipFamilies: + - IPv4 + - IPv6 + externalIPs: + - 10.200.34.53 + - 10.200.34.54 + sessionAffinity: ClientIP + sessionAffinityConfig: + clientIP: + timeoutSeconds: 100 + ports: + - name: port-name + port: 12345 + protocol: TCP + targetPort: 12345 + nodePort: 30000 + - name: port-name2 + port: 12344 + protocol: TCP + targetPort: 12346 + nodePort: 30001 + selector: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/name: common-test + pod.name: my-workload diff --git a/library/common-test/tests/service/validation_test.yaml b/library/common-test/tests/service/validation_test.yaml new file mode 100644 index 0000000000..580cc5065e --- /dev/null +++ b/library/common-test/tests/service/validation_test.yaml @@ -0,0 +1,391 @@ +suite: service validation test +templates: + - common.yaml +tests: + - it: should fail without primary service + set: + service: + service-name: + enabled: true + asserts: + - failedTemplate: + errorMessage: Service - At least one enabled service must be primary + + - it: should fail with more than one primary service + set: + service: + service-name1: + enabled: true + primary: true + service-name2: + enabled: true + primary: true + asserts: + - failedTemplate: + errorMessage: Service - Only one service can be primary + + - it: should fail without primary port in service + set: + service: + service-name1: + enabled: true + primary: true + ports: + port-name: + enabled: true + asserts: + - failedTemplate: + errorMessage: Service - At least one enabled port in service must be primary + + - it: should fail with more than one primary port in service + set: + service: + service-name1: + enabled: true + primary: true + ports: + port-name1: + enabled: true + primary: true + port-name2: + enabled: true + primary: true + asserts: + - failedTemplate: + errorMessage: Service - Only one port per service can be primary + + - it: should fail with no enabled ports in enabled service + set: + service: + service-name1: + enabled: true + primary: true + ports: + port-name1: + enabled: true + primary: true + port-name2: + enabled: true + primary: true + asserts: + - failedTemplate: + errorMessage: Service - Only one port per service can be primary + + - it: should fail with annotations not a dict + set: + service: + service-name1: + enabled: true + primary: true + annotations: not-a-dict + ports: + port-name1: + enabled: true + primary: true + asserts: + - failedTemplate: + errorMessage: Service - Expected to be a dictionary, but got [string] + + - it: should fail with labels not a dict + set: + service: + service-name1: + enabled: true + primary: true + labels: not-a-dict + ports: + port-name1: + enabled: true + primary: true + asserts: + - failedTemplate: + errorMessage: Service - Expected to be a dictionary, but got [string] + + - it: should fail with pod targetSelector not a string + set: + service: + service-name1: + enabled: true + primary: true + targetSelector: + pod: not-a-string + ports: + port-name1: + enabled: true + primary: true + asserts: + - failedTemplate: + errorMessage: Service - Expected to be [string], but got [map] + + - it: should fail with container targetSelector not a string + set: + service: + service-name1: + enabled: true + primary: true + ports: + port-name1: + enabled: true + primary: true + targetSelector: + container: not-a-string + asserts: + - failedTemplate: + errorMessage: Service - Expected to be [string], but got [map] + + - it: should fail with selected pod not defined + set: + service: + service-name1: + enabled: true + primary: true + targetSelector: some-pod-name + ports: + port-name: + enabled: true + primary: true + port: 12345 + workload: + main: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Service - Selected pod [some-pod-name] is not defined + + - it: should fail with selected pod not enabled + set: + service: + service-name1: + enabled: true + primary: true + targetSelector: some-pod-name + ports: + port-name: + enabled: true + primary: true + port: 12345 + workload: + some-pod-name: + enabled: false + primary: true + type: Deployment + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Service - Selected pod [some-pod-name] is not enabled + + - it: should fail with invalid port protocol + set: + service: + service-name1: + enabled: true + primary: true + ports: + port-name1: + enabled: true + primary: true + port: 12345 + protocol: not-a-protocol + asserts: + - failedTemplate: + errorMessage: Service - Expected to be one of [tcp, udp, http, https] but got [not-a-protocol] + + - it: should fail without port number + set: + service: + service-name1: + enabled: true + primary: true + ports: + port-name1: + enabled: true + primary: true + port: + asserts: + - failedTemplate: + errorMessage: Service - Expected non-empty + + - it: should fail with invalid service type + set: + service: + service-name1: + enabled: true + primary: true + type: not-a-type + ports: + port-name1: + enabled: true + primary: true + asserts: + - failedTemplate: + errorMessage: Service - Expected to be one of [ClusterIP, NodePort] but got [not-a-type] + + - it: should fail with invalid ipFamilyPolicy + set: + service: + service-name1: + enabled: true + primary: true + type: ClusterIP + ipFamilyPolicy: not-a-policy + ports: + port-name1: + enabled: true + primary: true + port: 12345 + workload: + some-pod-name: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Service - Expected to be one of [SingleStack, PreferDualStack, RequireDualStack], but got [not-a-policy] + + - it: should fail with ipFamilies not a list + set: + service: + service-name1: + enabled: true + primary: true + type: ClusterIP + ipFamilies: not-a-list + ports: + port-name1: + enabled: true + primary: true + port: 12345 + workload: + some-pod-name: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Service - Expected to be a list, but got a [string] + + - it: should fail with invalid ipFamilies + set: + service: + service-name1: + enabled: true + primary: true + type: ClusterIP + ipFamilies: + - not-a-family + ports: + port-name1: + enabled: true + primary: true + port: 12345 + workload: + some-pod-name: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Service - Expected to be one of [IPv4, IPv6], but got [not-a-family] + + - it: should fail with invalid sessionAffinity + set: + service: + service-name1: + enabled: true + primary: true + type: ClusterIP + sessionAffinity: not-an-affinity + ports: + port-name1: + enabled: true + primary: true + port: 12345 + workload: + some-pod-name: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Service - Expected to be one of [ClientIP, None], but got [not-an-affinity] + + - it: should fail with invalid timeoutSeconds in sessionAffinityConfig + set: + service: + service-name1: + enabled: true + primary: true + type: ClusterIP + sessionAffinity: ClientIP + sessionAffinityConfig: + clientIP: + timeoutSeconds: -1 + ports: + port-name1: + enabled: true + primary: true + port: 12345 + workload: + some-pod-name: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Service - Expected to be between [0 - 86400], but got [-1] + + - it: should fail without nodePort number on NodePort + set: + service: + service-name1: + enabled: true + primary: true + type: NodePort + ports: + port-name1: + enabled: true + primary: true + port: 80 + nodePort: + workload: + some-pod-name: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Service - Expected non-empty on NodePort service type + + - it: should fail with nodePort lower than the minimum on NodePort + set: + global: + minNodePort: 10000 + service: + service-name1: + enabled: true + primary: true + type: NodePort + ports: + port-name1: + enabled: true + primary: true + port: 80 + nodePort: 9999 + workload: + some-pod-name: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Service - Expected to be higher than [10000], but got [9999] diff --git a/library/common-test/tests/serviceAccount/metadata_test.yaml b/library/common-test/tests/serviceAccount/metadata_test.yaml new file mode 100644 index 0000000000..5bc77edef0 --- /dev/null +++ b/library/common-test/tests/serviceAccount/metadata_test.yaml @@ -0,0 +1,59 @@ +suite: service account metadata test +templates: + - common.yaml +chart: + appVersion: &appVer v9.9.9 +tests: + - it: should pass with service account created with labels and annotations + set: + label1: label1 + label2: global_label2 + annotation1: annotation1 + annotation2: global_annotation2 + global: + labels: + g_label1: global_label1 + g_label2: "{{ .Values.label2 }}" + annotations: + g_annotation1: global_annotation1 + g_annotation2: "{{ .Values.annotation2 }}" + serviceAccount: + my-sa1: + enabled: true + primary: true + labels: + label1: "{{ .Values.label1 }}" + label2: label2 + annotations: + annotation1: "{{ .Values.annotation1 }}" + annotation2: annotation2 + asserts: + - documentIndex: &serviceAccountDoc 0 + isKind: + of: ServiceAccount + - documentIndex: *serviceAccountDoc + equal: + path: metadata.annotations + value: + annotation1: annotation1 + annotation2: annotation2 + g_annotation1: global_annotation1 + g_annotation2: global_annotation2 + - documentIndex: *serviceAccountDoc + equal: + path: metadata.labels + value: + app: common-test-1.0.0 + release: RELEASE-NAME + helm-revision: "0" + helm.sh/chart: common-test-1.0.0 + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: *appVer + g_label1: global_label1 + g_label2: global_label2 + label1: label1 + label2: label2 + - documentIndex: *serviceAccountDoc + equal: + path: automountServiceAccountToken + value: false diff --git a/library/common-test/tests/serviceAccount/name_test.yaml b/library/common-test/tests/serviceAccount/name_test.yaml new file mode 100644 index 0000000000..899b13e0b5 --- /dev/null +++ b/library/common-test/tests/serviceAccount/name_test.yaml @@ -0,0 +1,45 @@ +suite: service account name test +templates: + - common.yaml +tests: + - it: should generate correct name + set: + serviceAccount: + my-sa: + enabled: true + primary: true + my-sa1: + enabled: true + my-sa2: + enabled: true + asserts: + - documentIndex: &primaryServiceAccount 0 + isKind: + of: ServiceAccount + - documentIndex: *primaryServiceAccount + isAPIVersion: + of: v1 + - documentIndex: *primaryServiceAccount + equal: + path: metadata.name + value: release-name-common-test + - documentIndex: &serviceAccount 1 + isKind: + of: ServiceAccount + - documentIndex: *serviceAccount + isAPIVersion: + of: v1 + - documentIndex: *serviceAccount + equal: + path: metadata.name + value: release-name-common-test-my-sa1 + - documentIndex: &otherServiceAccount 2 + isKind: + of: ServiceAccount + - documentIndex: *otherServiceAccount + isAPIVersion: + of: v1 + - documentIndex: *otherServiceAccount + equal: + path: metadata.name + value: release-name-common-test-my-sa2 diff --git a/library/common-test/tests/serviceAccount/validation_test.yaml b/library/common-test/tests/serviceAccount/validation_test.yaml new file mode 100644 index 0000000000..720334a3e9 --- /dev/null +++ b/library/common-test/tests/serviceAccount/validation_test.yaml @@ -0,0 +1,77 @@ +suite: service account validation test +templates: + - common.yaml +tests: + - it: should fail with name longer than 63 characters + set: + serviceAccount: + my-sa: + enabled: true + primary: true + my-service-account-super-long-name-that-is-longer-than-63-characters: + enabled: true + primary: false + asserts: + - failedTemplate: + errorMessage: Name [release-name-common-test-my-service-account-super-long-name-that-is-longer-than-63-characters] is not valid. Must start and end with an alphanumeric lowercase character. It can contain '-'. And must be at most 63 characters. + + - it: should fail with name starting with underscore + set: + serviceAccount: + my-sa1: + enabled: true + primary: true + _my-sa2: + enabled: true + primary: false + asserts: + - failedTemplate: + errorMessage: Name [release-name-common-test-_my-sa2] is not valid. Must start and end with an alphanumeric lowercase character. It can contain '-'. And must be at most 63 characters. + + - it: should fail with labels not a dict + set: + serviceAccount: + my-sa: + enabled: true + primary: true + labels: "not a dict" + asserts: + - failedTemplate: + errorMessage: Service Account - Expected to be a dictionary, but got [string] + + - it: should fail with annotations not a dict + set: + serviceAccount: + my-sa: + enabled: true + primary: true + annotations: "not a dict" + asserts: + - failedTemplate: + errorMessage: Service Account - Expected to be a dictionary, but got [string] + + - it: should fail with more than 1 primary service accounts + set: + serviceAccount: + my-sa: + enabled: true + primary: true + my-sa2: + enabled: true + primary: true + asserts: + - failedTemplate: + errorMessage: Service Account - Only one service account can be primary + + - it: should fail without any primary on enabled service accounts + set: + serviceAccount: + my-sa: + enabled: true + primary: false + my-sa2: + enabled: true + primary: false + asserts: + - failedTemplate: + errorMessage: Service Account - At least one enabled service account must be primary diff --git a/library/common-test/tests/workload/names_test.yaml b/library/common-test/tests/workload/names_test.yaml new file mode 100644 index 0000000000..34eb780350 --- /dev/null +++ b/library/common-test/tests/workload/names_test.yaml @@ -0,0 +1,54 @@ +suite: workload name test +templates: + - common.yaml +tests: + - it: should generate correct workload name + set: + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: {} + cronjob-workload-name: + enabled: true + primary: false + type: CronJob + schedule: "*/1 * * * *" + podSpec: {} + job-workload-name: + enabled: true + primary: false + type: Job + podSpec: {} + asserts: + - documentIndex: &cronJobDoc 0 + isKind: + of: CronJob + - documentIndex: *cronJobDoc + isAPIVersion: + of: batch/v1 + - documentIndex: *cronJobDoc + equal: + path: metadata.name + value: release-name-common-test-cronjob-workload-name + - documentIndex: &jobDoc 1 + isKind: + of: Job + - documentIndex: *jobDoc + isAPIVersion: + of: batch/v1 + - documentIndex: *jobDoc + equal: + path: metadata.name + value: release-name-common-test-job-workload-name + - documentIndex: &deploymentDoc 2 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + equal: + path: metadata.name + value: release-name-common-test diff --git a/library/common-test/tests/workload/validation_test.yaml b/library/common-test/tests/workload/validation_test.yaml new file mode 100644 index 0000000000..653f32c11e --- /dev/null +++ b/library/common-test/tests/workload/validation_test.yaml @@ -0,0 +1,120 @@ +suite: workload validation test +templates: + - common.yaml +tests: + - it: should fail with name longer than 63 characters + set: + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: {} + other-workload-name-super-long-name-that-is-longer-than-63-characters: + enabled: true + primary: false + type: Deployment + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Name [release-name-common-test-other-workload-name-super-long-name-that-is-longer-than-63-characters] is not valid. Must start and end with an alphanumeric lowercase character. It can contain '-'. And must be at most 63 characters. + + - it: should fail with name starting with underscore + set: + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: {} + _other-workload-name: + enabled: true + primary: false + type: Deployment + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Name [release-name-common-test-_other-workload-name] is not valid. Must start and end with an alphanumeric lowercase character. It can contain '-'. And must be at most 63 characters. + + - it: should fail with invalid type + set: + workload: + workload-name: + enabled: true + primary: true + type: not-valid-type + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Workload - Expected to be one of [Deployment, Job, CronJob], but got [not-valid-type] + + - it: should fail without podSpec + set: + workload: + workload-name: + enabled: true + primary: true + type: Deployment + asserts: + - failedTemplate: + errorMessage: Workload - Expected key to exist + + - it: should fail without primary enabled workload + set: + workload: + workload-name: + enabled: false + primary: true + type: Deployment + podSpec: {} + other-workload-name: + enabled: true + primary: false + type: Deployment + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Workload - One enabled workload must be primary + + - it: should fail more than one primary enabled workload + set: + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: {} + other-workload-name: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Workload - Only one workload can be primary + + - it: should fail with labels not a dict + set: + workload: + workload-name: + enabled: true + primary: true + type: Deployment + labels: "not a dict" + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Workload - Expected to be a dictionary, but got [string] + + - it: should fail with annotations not a dict + set: + workload: + workload-name: + enabled: true + primary: true + type: Deployment + annotations: "not a dict" + podSpec: {} + asserts: + - failedTemplate: + errorMessage: Workload - Expected to be a dictionary, but got [string] diff --git a/library/common-test/values.yaml b/library/common-test/values.yaml new file mode 100644 index 0000000000..bed9ba4811 --- /dev/null +++ b/library/common-test/values.yaml @@ -0,0 +1,33 @@ +# This file disables all defaults for the common-test chart. +# This is to have common test cases without getting affected +# by any default values. + +workload: + main: + enabled: false + podSpec: + containers: + main: + enabled: false + +service: + main: + enabled: false + ports: + main: + enabled: false + +# The above just disables the "main" objects, +# every other config is stays as it is set in common's values.yaml. +# Tests need to use a different name, other than `main`, +# so they don't "inherit" values from common. +# The above just makes it easier to test with specific values, +# set on each test case, without having to worry about pre-defined values. + +# TODO: +# It will be a test case using a different values.yaml (default-values.yaml), +# that do not disable any objects. So we can make sure that defaults apply. + +# Everything disabled above, must be explicitly enabled in the default-values.yaml +# As helm first applies values.yaml and then the specified values file, +# in this case default-values.yaml diff --git a/library/common/1.0.0/Chart.yaml b/library/common/1.0.0/Chart.yaml new file mode 100644 index 0000000000..591a2254bb --- /dev/null +++ b/library/common/1.0.0/Chart.yaml @@ -0,0 +1,10 @@ +apiVersion: v2 +title: Common Library Chart +name: common +description: A library chart for iX Official Catalog +type: library +version: 1.0.0 +appVersion: v1 +maintainers: + - name: truenas + url: https://www.truenas.com/ diff --git a/library/common/1.0.0/README.md b/library/common/1.0.0/README.md new file mode 100644 index 0000000000..f4615d7723 --- /dev/null +++ b/library/common/1.0.0/README.md @@ -0,0 +1,24 @@ +# Common Library + +## Naming Scheme + +- ServiceAccount: + - Primary: `$FullName` + - Others: `$FullName-$ServiceAccountName` +- RBAC: + - Primary: `$FullName` + - Others: `$FullName-$RBACName` +- Service: + - Primary: `$FullName` + - Others: `$FullName-$ServiceName` +- Pods: + - Primary: `$FullName` + - Others: `$FullName-$PodName` +- Containers: `$ContainerName` +- ConfigMap: `$FullName-$ConfigMapName` +- Secret: `$FullName-$SecretName` +- Scale Certificate: `$FullName-$CertName` +- Scale External Interface: `ix-$ReleaseName-$index` + +> Full name -> `$ReleaseName-$ChartName` +> Any name that exceeds 63 characters, will throw an error diff --git a/library/common/1.0.0/docs/README.md b/library/common/1.0.0/docs/README.md new file mode 100644 index 0000000000..6b09e5d49d --- /dev/null +++ b/library/common/1.0.0/docs/README.md @@ -0,0 +1,285 @@ +# Common Chart Documentation + +## Global and Defaults + +This options should not need to be changed per chart. + +| Key | Type | Required | Helm Template | Default | Description | +| :----------------------------------------------------------------- | :------: | :------: | :----------------: | :---------: | :----------------------------------------------------------- | +| .Values.global.labels | `dict` | ❌ | ✅ (On value only) | `{}` | Additional Labels that apply to all objects | +| .Values.global.annotations | `dict` | ❌ | ✅ (On value only) | `{}` | Additional Annotations that apply to all objects | +| .Values.global.minNodePort | `int` | ✅ | ❌ | `9000` | Minimum Node Port Allowed | +| .Values.fallbackDefaults.probeType | `string` | ✅ | ❌ | `http` | Default probe type when not defined in the container level | +| .Values.fallbackDefaults.serviceProtocol | `string` | ✅ | ❌ | `tcp` | Default service protocol when not defined in the service | +| .Values.fallbackDefaults.serviceType | `string` | ✅ | ❌ | `ClusterIP` | Default service type when not defined in the service | +| .Values.fallbackDefaults.persistenceType | `string` | ✅ | ❌ | `emptyDir` | Default persistence type when not defined in the persistence | +| .Values.fallbackDefaults.probeTimeouts | `dict` | ✅ | ❌ | See below | Default probe timeouts if not defined in the container | +| .Values.fallbackDefaults.probeTimeouts.[probe] | `dict` | ✅ | ❌ | See below | Default probe timeouts if not defined in the container | +| .Values.fallbackDefaults.probeTimeouts.[probe].initialDelaySeconds | `int` | ✅ | ❌ | See below | Default initialDelaySeconds if not defined in the container | +| .Values.fallbackDefaults.probeTimeouts.[probe].periodSeconds | `int` | ✅ | ❌ | See below | Default periodSeconds if not defined in the container | +| .Values.fallbackDefaults.probeTimeouts.[probe].timeoutSeconds | `int` | ✅ | ❌ | See below | Default timeoutSeconds if not defined in the container | +| .Values.fallbackDefaults.probeTimeouts.[probe].failureThreshold | `int` | ✅ | ❌ | See below | Default failureThreshold if not defined in the container | +| .Values.fallbackDefaults.probeTimeouts.[probe].successThreshold | `int` | ✅ | ❌ | See below | Default successThreshold if not defined in the container | + +--- + +Default probe timeouts: + +```yaml +probeTimeouts: + liveness: + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 5 + successThreshold: 1 + readiness: + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 5 + successThreshold: 2 + startup: + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 2 + failureThreshold: 60 + successThreshold: 1 +``` + +--- + +Examples: + +```yaml +global: + labels: + key: value + keytpl: "{{ .Values.some.value }}" + annotations: + key: value + keytpl: "{{ .Values.some.value }}" + minNodePort: 9000 + +faillbackDefaults: + probeType: http + serviceProtocol: tcp + serviceType: ClusterIP + persistenceType: emptyDir + probeTimeouts: + liveness: + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 5 + successThreshold: 1 + readiness: + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 5 + successThreshold: 2 + startup: + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 2 + failureThreshold: 60 + successThreshold: 1 +``` + +--- + +## Global Values that apply on pods/containers + +All of the below values are applied on all pods/containers, but can be overridden on the pod/container level. +This is so, you can have a single point to define the values from the scale UI, +but still have the ability to override them on the pod/container level, in case you need to. + +| Key | Type | Required | Helm Template | Default | Description | +| :------------------------------------------------- | :-------: | :------: | :-----------: | :-------: | :--------------------------------------------------------------------- | +| .Values.TZ | `string` | ✅ | ❌ | See below | Timezone that is used everywhere applicable | +| .Values.PUID | `int` | ✅ | ❌ | See below | PUID (Only applied when running as root) | +| .Values.UMASK | `string` | ✅ | ❌ | See below | UMASK | +| .Values.NVIDIA_CAPS | `list` | ✅ | ❌ | See below | NVIDIA_CAPS (Only applied when scaleGPU is passed) | +| .Values.containerOptions | `dict` | ✅ | ❌ | See below | Options that apply to all containers | +| .Values.containerOptions.resources | `dict` | ✅ | ❌ | See below | Resources | +| .Values.containerOptions.resources.limits | `dict` | ✅ | ❌ | See below | Resources | +| .Values.containerOptions.resources.limits.cpu | `string` | ✅ | ❌ | See below | Resources | +| .Values.containerOptions.resources.limits.memory | `string` | ✅ | ❌ | See below | Resources | +| .Values.containerOptions.resources.requests | `dict` | ✅ | ❌ | See below | Resources | +| .Values.containerOptions.resources.requests.cpu | `string` | ✅ | ❌ | See below | Resources | +| .Values.containerOptions.resources.requests.memory | `string` | ✅ | ❌ | See below | Resources | +| .Values.podOptions | `dict` | ✅ | ❌ | See below | Options that apply to all pods | +| .Values.podOptions.enableServiceLinks | `boolean` | ✅ | ❌ | See below | enableServiceLinks | +| .Values.podOptions.hostNetwork | `boolean` | ✅ | ❌ | See below | hostNetwork | +| .Values.podOptions.restartPolicy | `string` | ✅ | ❌ | See below | restartPolicy | +| .Values.podOptions.dnsPolicy | `string` | ✅ | ❌ | See below | dnsPolicy | +| .Values.podOptions.dnsConfig | `list` | ✅ | ❌ | See below | dnsConfig | +| .Values.podOptions.hostAliases | `list` | ✅ | ❌ | See below | hostAliases | +| .Values.podOptions.tolerations | `list` | ✅ | ❌ | See below | tolerations | +| .Values.podOptions.runtimeClassName | `string` | ✅ | ❌ | See below | runtimeClassName (value in ixChartContext will always take precedence) | +| .Values.podOptions.automountServiceAccountToken | `boolean` | ✅ | ❌ | See below | automountServiceAccountToken | +| .Values.podOptions.terminationGracePeriodSeconds | `int` | ✅ | ❌ | See below | terminationGracePeriodSeconds | + + + +--- + +Defaults: + +```yaml +TZ: UTC +PUID: 568 +UMASK: "002" +NVIDIA_CAPS: + - all +containerOptions: + resources: + limits: + cpu: 4000m + memory: 8Gi + requests: + cpu: 10m + memory: 50Mi +podOptions: + enableServiceLinks: false + hostNetwork: false + restartPolicy: Always + dnsPolicy: ClusterFirst + dnsConfig: + options: + - name: ndots + value: "2" + hostAliases: [] + tolerations: [] + runtimeClassName: "" + automountServiceAccountToken: false + terminationGracePeriodSeconds: 120 +``` + +--- + +## Global Security Context + +All of the below values are applied on all pods/containers, but can be overridden on the pod/container level. +This is so, you can have a single point to define the values from the scale UI, +but still have the ability to override them on the pod/container level, in case you need to. + +| Key | Type | Required | Helm Template | Default | Description | +| :--------------------------------------------------------- | :-------: | :------: | :-----------: | :-------: | :------------------------------ | +| .Values.securityContext | `dict` | ✅ | ❌ | See below | Security Context | +| .Values.securityContext.container | `dict` | ✅ | ❌ | See below | Security Context for containers | +| .Values.securityContext.container.runAsNonRoot | `boolean` | ✅ | ❌ | See below | | +| .Values.securityContext.container.runAsUser | `int` | ✅ | ❌ | See below | | +| .Values.securityContext.container.runAsGroup | `int` | ✅ | ❌ | See below | | +| .Values.securityContext.container.readOnlyRootFilesystem | `boolean` | ✅ | ❌ | See below | | +| .Values.securityContext.container.allowPrivilegeEscalation | `boolean` | ✅ | ❌ | See below | | +| .Values.securityContext.container.privileged | `boolean` | ✅ | ❌ | See below | | +| .Values.securityContext.container.seccompProfile | `dict` | ✅ | ❌ | See below | | +| .Values.securityContext.container.seccompProfile.type | `string` | ✅ | ❌ | See below | | +| .Values.securityContext.container.seccompProfile.profile | `string` | ✅ | ❌ | See below | | +| .Values.securityContext.container.capabilities | `dict` | ✅ | ❌ | See below | | +| .Values.securityContext.container.capabilities.add | `list` | ✅ | ❌ | See below | | +| .Values.securityContext.container.capabilities.drop | `list` | ✅ | ❌ | See below | | +| .Values.securityContext.pod | `dict` | ✅ | ❌ | See below | Security Context for pods | +| .Values.securityContext.pod.fsGroup | `int` | ✅ | ❌ | See below | | +| .Values.securityContext.pod.fsGroupChangePolicy | `string` | ✅ | ❌ | See below | | +| .Values.securityContext.pod.supplementalGroup | `list` | ✅ | ❌ | See below | | +| .Values.securityContext.pod.sysctls | `list` | ✅ | ❌ | See below | | + + + +Defaults: + +```yaml +securityContext: + # -- Container security context for all containers + # Can be overruled per container + container: + runAsNonRoot: true + runAsUser: 568 + runAsGroup: 568 + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + privileged: false + seccompProfile: + type: RuntimeDefault + capabilities: + add: [] + drop: + - ALL + # -- Pod security context for all pods + # Can be overruled per pod + pod: + fsGroup: 568 + fsGroupChangePolicy: OnRootMismatch + supplementalGroups: [] + sysctls: [] +``` + +--- + +## Images + +| Key | Type | Required | Helm Template | Default | Description | +| :----------------------- | :------: | :------: | :-----------: | :-------: | :---------------- | +| .Values.image | `dict` | ✅ | ❌ | See below | Image | +| .Values.image.repository | `string` | ✅ | ❌ | See below | Image Repository | +| .Values.image.tag | `string` | ✅ | ❌ | See below | Image Tag | +| .Values.image.pullPolicy | `string` | ✅ | ❌ | See below | Image Pull Policy | + + + +--- + +Defaults: + +```yaml +image: + repository: "" + tag: "" + pullPolicy: IfNotPresent +``` + +You can define additional images using the following convention: + +```yaml +imageWorker: + repository: "" + tag: "" + pullPolicy: IfNotPresent +``` + +There isn't anything special in the above format, it's just a convention. +It's also a format that some external tools can use for automatic image updates. +For example, `Renovate` + +--- + +Additional Documentation: + +- [workload](workload/README.md) +- [container](container/README.md) +- [service](service/README.md) +- [persistence](persistence/README.md) +- [configmap](configmap.md) +- [secret](secret.md) +- [imagePullSecrets](imagePullSecrets.md) +- [serviceAccount](serviceAccount.md) +- [rbac](rbac.md) +- [scaleGPU](scaleGPU.md) +- [scaleCertificate](scaleCertificate.md) +- [scaleExternalInterface](scaleExternalInterface.md) +- [notes](notes.md) + +--- + +Notes: + +This applies across all the documentation: + +- Helm Template: + - `❌` means that the value is not templated + - `✅` means that the value is templated, + for example instead of a hardcoded value, you can set it to `{{ .Values.some.value }}`. + and it will be replaced by the value contained in `.Values.some.value` at the installation/upgrade time. diff --git a/library/common/1.0.0/docs/configmap.md b/library/common/1.0.0/docs/configmap.md new file mode 100644 index 0000000000..9984249e04 --- /dev/null +++ b/library/common/1.0.0/docs/configmap.md @@ -0,0 +1,48 @@ +# ConfigMap + +| Key | Type | Required | Helm Template | Default | Description | +| :------------------------------------- | :-------: | :------: | :----------------: | :-----: | :----------------------------------- | +| configmap | `dict` | ❌ | ❌ | `{}` | Define the configMaps as dicts | +| configmap.[configmap-name] | `dict` | ✅ | ❌ | `{}` | Holds configMap definition | +| configmap.[configmap-name].enabled | `boolean` | ✅ | ❌ | `false` | Enables or Disables the configMap | +| configmap.[configmap-name].labels | `dict` | ❌ | ✅ (On value only) | `{}` | Additional labels for configmap | +| configmap.[configmap-name].annotations | `dict` | ❌ | ✅ (On value only) | `{}` | Additional annotations for configmap | +| configmap.[configmap-name].data | `dict` | ✅ | ✅ | `{}` | Define the data of the configmap | + +--- + +Appears in: + +- `.Values.configmap` + +--- + +Naming scheme: + +- `$FullName-$ConfigmapName` (release-name-chart-name-configmapName) + +--- + +Examples: + +```yaml +configmap: + + configmap-name: + enabled: true + labels: + key: value + keytpl: "{{ .Values.some.value }}" + annotations: + key: value + keytpl: "{{ .Values.some.value }}" + data: + key: value + + other-configmap-name: + enabled: true + data: + key: | + multi line + text value +``` diff --git a/library/common/1.0.0/docs/container/README.md b/library/common/1.0.0/docs/container/README.md new file mode 100644 index 0000000000..c8fed28d51 --- /dev/null +++ b/library/common/1.0.0/docs/container/README.md @@ -0,0 +1,93 @@ +# Container + +Assume every key below has a prefix of `workload.[workload-name].podSpec`. + +| Key | Type | Required | Helm Template | Default | Description | +| :---------------------------------- | :-------: | :------: | :-----------: | :-----: | :-------------------------------- | +| containers.[container-name] | `dict` | ✅ | ❌ | `{}` | Define the container as dict | +| containers.[container-name].enabled | `boolean` | ✅ | ❌ | `false` | Enables or Disables the container | +| containers.[container-name].primary | `boolean` | ✅ | ❌ | `false` | Sets the container as primary | +| containers.[container-name].stdin | `boolean` | ❌ | ❌ | `false` | whether to enable stdin or not | +| containers.[container-name].tty | `boolean` | ❌ | ❌ | `false` | whether to enable tty or not | + +--- + +Appears in: + +- `.Values.workload.[workload-name].podSpec.containers.[container-name]` + +--- + +Naming scheme: + +- Primary: `$FullName` (release-name-chart-name) +- Non-Primary: `$FullName-$ContainerName` (release-name-chart-name-container-name) + +--- + +More keys for `container` can be found below: + +- [command](command.md) +- [args](args.md) +- [termination](termination.md) +- [lifecycle](lifecycle.md) +- [probes](probes.md) +- [resources](resources.md) +- [securityContext](securityContext.md) +- [envFrom](envFrom.md) +- [fixedEnv](fixedEnv.md) +- [env](env.md) +- [envList](envList.md) + +--- + +Examples: + +```yaml +workload: + workload-name: + enabled: true + primary: true + podSpec: + containers: + container-name: + enabled: true + primary: true + stdin: true + tty: true +``` + +## InitContainer + +| Key | Type | Required | Helm Template | Default | Description | +| :-------------------------------------- | :-------: | :------: | :-----------: | :-----: | :----------------------------------------------------- | +| initContainers.[container-name] | `dict` | ✅ | ❌ | `{}` | Define the initContainer as dict | +| initContainers.[container-name].enabled | `boolean` | ✅ | ✅ | `false` | Enables or Disables the initContainer | +| initContainers.[container-name].type | `string` | ✅ | ✅ | `{}` | Define the type initContainer (init, install, upgrade) | + +> Supports all keys from [container](container.md) +> Does not use `primary` key, `lifecycle` key and `probes` key + +--- + +Notes: + +`init` type run before the containers is started. +`install` type run before the containers is started and only on install. +`upgrade` type run before the containers is started and only on upgrade. + +--- + +Examples: + +```yaml +workload: + workload-name: + enabled: true + primary: true + podSpec: + initContainers: + container-name: + enabled: true + # ... +``` diff --git a/library/common/1.0.0/docs/container/args.md b/library/common/1.0.0/docs/container/args.md new file mode 100644 index 0000000000..f63793bc41 --- /dev/null +++ b/library/common/1.0.0/docs/container/args.md @@ -0,0 +1,33 @@ +# Args + +Assume every key below has a prefix of `workload.[workload-name].podSpec.containers.[container-name]`. + +| Key | Type | Required | Helm Template | Default | Description | +| :-------- | :-----------: | :------: | :-----------: | :-----: | :--------------------------------------------------------------------------------------------- | +| args | `list/string` | ❌ | ✅ | `[]` | Define arg(s). If it's single, can be defined as string | +| extraArgs | `list/string` | ❌ | ✅ | `[]` | Define extraArg(s). Those are appended after the `args`. Useful for user defined args from GUI | + +--- + +Appears in: + +- `.Values.workload.[workload-name].podSpec.containers.[container-name].args` + +--- + +Examples: + +```yaml +workload: + workload-name: + enabled: true + primary: true + podSpec: + containers: + container-name: + enabled: true + primary: true + args: arg + extraArgs: + - extraArg +``` diff --git a/library/common/1.0.0/docs/container/command.md b/library/common/1.0.0/docs/container/command.md new file mode 100644 index 0000000000..7bf773a133 --- /dev/null +++ b/library/common/1.0.0/docs/container/command.md @@ -0,0 +1,35 @@ +# Command + +Assume every key below has a prefix of `workload.[workload-name].podSpec.containers.[container-name]`. + +| Key | Type | Required | Helm Template | Default | Description | +| :------ | :-----------: | :------: | :-----------: | :-----: | :---------------------------------------------------------- | +| command | `list/string` | ❌ | ✅ | `[]` | Define command(s). If it's single, can be defined as string | + +--- + +Appears in: + +- `.Values.workload.[workload-name].podSpec.containers.[container-name].command` + +--- + +Examples: + +```yaml +workload: + workload-name: + enabled: true + primary: true + podSpec: + containers: + container-name: + enabled: true + primary: true + # As a list + command: + - command1 + - command2 + # As a string + command: command +``` diff --git a/library/common/1.0.0/docs/container/env.md b/library/common/1.0.0/docs/container/env.md new file mode 100644 index 0000000000..f5d1109cf8 --- /dev/null +++ b/library/common/1.0.0/docs/container/env.md @@ -0,0 +1,64 @@ +# Env + +Assume every key below has a prefix of `workload.[workload-name].podSpec.containers.[container-name]`. + +| Key | Type | Required | Helm Template | Default | Description | +| :----------------------------------------- | :-------: | :------: | :-------------: | :-----: | :------------------------------------------------------------------- | +| env | `dict` | ❌ | ❌ | `{}` | Define env(s) for the container | +| env.[key] | `string` | ✅ | ✅ (Only value) | `""` | Define the env key/value | +| env.[key].configMapKeyRef | `dict` | ❌ | ❌ | `{}` | Define variable from configMapKeyRef | +| env.[key].configMapKeyRef.name | `string` | ✅ | ✅ | `""` | Define the configMap name | +| env.[key].configMapKeyRef.key | `string` | ✅ | ❌ | `""` | Define the configMap key | +| env.[key].configMapKeyRef.expandObjectName | `boolean` | ❌ | ❌ | `true` | Whether to expand (adding the fullname as prefix) the configmap name | +| env.[key].secretKeyRef | `dict` | ❌ | ❌ | `{}` | Define secretKeyRef variable | +| env.[key].secretKeyRef.name | `string` | ✅ | ✅ | `""` | Define the secret name | +| env.[key].secretKeyRef.key | `string` | ✅ | ❌ | `""` | Define the secret key | +| env.[key].secretKeyRef.expandObjectName | `boolean` | ❌ | ❌ | `true` | Whether to expand (adding the fullname as prefix) the secret name | +| env.[key].fieldRef | `dict` | ❌ | ❌ | `{}` | Define fieldRef variable | +| env.[key].fieldRef.fieldPath | `string` | ✅ | ❌ | `""` | Define field path | +| env.[key].fieldRef.apiVersion | `string` | ❌ | ❌ | `""` | Define apiVersion | + +> Environment variables defined in `env` will be scanned for duplicate keys +> between other secrets/configmaps/env/envList/fixedEnv and will throw an error if it finds any. +> `secretKeyRef` and `configMapKeyRef` with `expandObjectName` set to `true` will also be validated that +> the actual objects are defined and have the specified key. +> `expandObjectName` should only be set to `false` if you want to consume a secret/configmap created outside of this chart + +--- + +Appears in: + +- `.Values.workload.[workload-name].podSpec.containers.[container-name].env` + +--- + +Examples: + +```yaml +workload: + workload-name: + enabled: true + primary: true + podSpec: + containers: + container-name: + enabled: true + primary: true + env: + ENV_NAME1: ENV_VALUE + ENV_NAME2: "{{ .Values.some.path }}" + ENV_NAME3: + configMapKeyRef: + # This will be expanded to 'fullname-configmap-name' + name: configmap-name + key: configmap-key + ENV_NAME4: + secretKeyRef: + name: secret-name + key: secret-key + expandObjectName: false + ENV_NAME5: + fieldRef: + fieldPath: metadata.name + apiVersion: v1 +``` diff --git a/library/common/1.0.0/docs/container/envFrom.md b/library/common/1.0.0/docs/container/envFrom.md new file mode 100644 index 0000000000..dd497fe8d5 --- /dev/null +++ b/library/common/1.0.0/docs/container/envFrom.md @@ -0,0 +1,46 @@ +# Env From + +Assume every key below has a prefix of `workload.[workload-name].podSpec.containers.[container-name]`. + +| Key | Type | Required | Helm Template | Default | Description | +| :------------------------------------ | :-------: | :------: | :-----------: | :-----: | :------------------------------------------------------------------- | +| envFrom | `list` | ❌ | ❌ | `{}` | Define envFrom for the container | +| envFrom.secretRef | `dict` | ✅ | ❌ | `{}` | Define the secretRef | +| envFrom.secretRef.name | `string` | ✅ | ✅ | `""` | Define the secret name | +| envFrom.secretRef.expandObjectName | `boolean` | ❌ | ❌ | `true` | Whether to expand (adding the fullname as prefix) the secret name | +| envFrom.configMapRef | `dict` | ✅ | ❌ | `{}` | Define the configMapRef | +| envFrom.configMapRef.name | `string` | ✅ | ✅ | `""` | Define the configmap name | +| envFrom.configMapRef.expandObjectName | `boolean` | ❌ | ❌ | `true` | Whether to expand (adding the fullname as prefix) the configmap name | + +> When the `expandObjectName` is `true`, it will also scan the contents of the secret/configmap +> for duplicate keys between other secrets/configmaps/env/envList/fixedEnv and will throw an error if it finds any. +> `expandObjectName` should only be set to `false` if you want to consume a secret/configmap created outside of this chart + +--- + +Appears in: + +- `.Values.workload.[workload-name].podSpec.containers.[container-name].envFrom` + +--- + +Examples: + +```yaml +workload: + workload-name: + enabled: true + primary: true + podSpec: + containers: + container-name: + enabled: true + primary: true + envFrom: + - secretRef: + # This will be expanded to `fullname-secret-name` + name: secret-name + - configMapRef: + name: configmap-name + expandObjectName: false +``` diff --git a/library/common/1.0.0/docs/container/envList.md b/library/common/1.0.0/docs/container/envList.md new file mode 100644 index 0000000000..101972330c --- /dev/null +++ b/library/common/1.0.0/docs/container/envList.md @@ -0,0 +1,42 @@ +# Env List + +Assume every key below has a prefix of `workload.[workload-name].podSpec.containers.[container-name]`. + +| Key | Type | Required | Helm Template | Default | Description | +| :------------ | :------: | :------: | :-----------: | :-----: | :------------------------------- | +| envList | `list` | ❌ | ❌ | `[]` | Define env(s) for the container | +| envList.name | `string` | ✅ | ❌ | `""` | Define the env name | +| envList.value | `string` | ❌ | ✅ | `""` | Define the env value | + +> `envList` is used for the SCALE GUI for "Additional Environment Variables" +> Environment variables defined in `envList` will be scanned for duplicate keys +> between other secrets/configmaps/env/envList/fixedEnv and will throw an error if it finds any. + +--- + +Appears in: + +- `.Values.workload.[workload-name].podSpec.containers.[container-name].envList` + +--- + +Examples: + +```yaml +workload: + workload-name: + enabled: true + primary: true + podSpec: + containers: + container-name: + enabled: true + primary: true + envList: + - name: ENV_NAME1 + value: ENV_VALUE + - name: ENV_NAME2 + value: "{{ .Values.some.path }}" + - name: ENV_NAME3 + value: "" +``` diff --git a/library/common/1.0.0/docs/container/fixedEnv.md b/library/common/1.0.0/docs/container/fixedEnv.md new file mode 100644 index 0000000000..1294ffdca5 --- /dev/null +++ b/library/common/1.0.0/docs/container/fixedEnv.md @@ -0,0 +1,59 @@ +# Fixed Env + +Assume every key below has a prefix of `workload.[workload-name].podSpec.containers.[container-name]`. + +| Key | Type | Required | Helm Template | Default | Description | +| :------------------- | :------: | :------: | :-----------: | :------------------------------------------: | :---------------------------------------------------------------------------- | +| fixedEnv | `dict` | ❌ | ❌ | `{}` | Override fixed Envs for the container | +| fixedEnv.TZ | `string` | ❌ | ❌ | `{{ .Values.TZ }}` | Override default TZ for the container | +| fixedEnv.UMASK | `string` | ❌ | ❌ | `{{ .Values.containerOptions.UMASK }}` | Override the default UMASK for the container (Applies to UMASK and UMASK_SET) | +| fixedEnv.PUID | `string` | ❌ | ❌ | `{{ .Values.containerOptions.PUID }}` | Override the default PUID for the container (Applies to PUID. USER_ID, UID) | +| fixedEnv.NVIDIA_CAPS | `list` | ❌ | ❌ | `{{ .Values.containerOptions.NVIDIA_CAPS }}` | Override the default NVIDIA_CAPS for the container, each entry is a string | + +> Environment variables in `fixedEnv` will be scanned for duplicate keys +> between other secrets/configmaps/env/envList and will throw an error if it finds any. + +--- + +Notes: + +By default it will set the following environment variables: + +- TZ: `{{ .Values.TZ }}` (or the value set in the container level under `fixedEnv`) +- UMASK: `{{ .Values.containerOptions.UMASK }}` (or the value set in the container level under `fixedEnv`) +- UMASK_SET: `{{ .Values.containerOptions.UMASK }}` (or the value set in the container level under `fixedEnv`) +- S6_READ_ONLY_ROOT: `1` (Only when `readOnlyRootFilesystem` or `runAsNonRoot` is `true`) +- PUID, USER_ID, UID: `{{ .Values.containerOptions.PUID }}` (or the value set in the container level under `fixedEnv`) + - Only when `runAsUser` or `runAsGroup` is `0` +- PGID, GROUP_ID, GID: To the `fsGroup` set for the pod (Either the default or the overrided value) + - Only when `runAsUser` or `runAsGroup` is `0` +- NVIDIA_DRIVER_CAPABILITIES: `{{ .Values.containerOptions.NVIDIA_CAPS }}` (or the value set in the container level under `fixedEnv`) + - Only when `scaleGPU` is assigned to the container + +--- + +Appears in: + +- `.Values.workload.[workload-name].podSpec.containers.[container-name].fixedEnv` + +--- + +Examples: + +```yaml +workload: + workload-name: + enabled: true + primary: true + podSpec: + containers: + container-name: + enabled: true + primary: true + fixedEnv: + TZ: "America/New_York" + NVIDIA_CAPS: + - compute + UMASK: "003" + PUID: "0" +``` diff --git a/library/common/1.0.0/docs/container/lifecycle.md b/library/common/1.0.0/docs/container/lifecycle.md new file mode 100644 index 0000000000..f45a75a08f --- /dev/null +++ b/library/common/1.0.0/docs/container/lifecycle.md @@ -0,0 +1,49 @@ +# Lifecycle + +Assume every key below has a prefix of `workload.[workload-name].podSpec.containers.[container-name]`. + +| Key | Type | Required | Helm Template | Default | Description | +| :--------------------------- | :-----------: | :---------------: | :----------------: | :-----: | :---------------------------------------------------------------------------------------- | +| lifecycle | `dict` | ❌ | ❌ | `{}` | Define lifecycle for the container | +| lifecycle.preStop | `dict` | ❌ | ❌ | `{}` | Define preStop lifecycle | +| lifecycle.postStart | `dict` | ❌ | ❌ | `{}` | Define preStop lifecycle | +| lifecycle.[hook].type | `string` | ❌ | ❌ | `""` | Define hook type (exec, http, https) (Used as a scheme in http(s) types) | +| lifecycle.[hook].command | `list/string` | ✅ (On exec type) | ✅ | `""` | Define command(s). If it's single, can be defined as string (Only when exec type is used) | +| lifecycle.[hook].port | `int` | ✅ (On http type) | ✅ | `""` | Define the port, (Only when http(s) type is used) | +| lifecycle.[hook].host | `string` | ❌ | ✅ | | Define the host, k8s defaults to POD IP (Only when http(s) type is used) | +| lifecycle.[hook].path | `string` | ❌ | ✅ | `/` | Define the path (Only when http(s) type is used) | +| lifecycle.[hook].httpHeaders | `dict` | ❌ | ✅ (On value only) | `{}` | Define the httpHeaders in key-value pairs (Only when http(s) type is used) | + +--- + +Appears in: + +- `.Values.workload.[workload-name].podSpec.containers.[container-name].lifecycle` + +--- + +Examples: + +```yaml +workload: + workload-name: + enabled: true + primary: true + podSpec: + containers: + container-name: + enabled: true + primary: true + lifecycle: + preStop: + type: exec + command: + - command + postStart: + type: http + port: 8080 + host: localhost + path: /path + httpHeaders: + key: value +``` diff --git a/library/common/1.0.0/docs/container/probes.md b/library/common/1.0.0/docs/container/probes.md new file mode 100644 index 0000000000..46832cb9f1 --- /dev/null +++ b/library/common/1.0.0/docs/container/probes.md @@ -0,0 +1,80 @@ +# Probes + +Assume every key below has a prefix of `workload.[workload-name].podSpec.containers.[container-name]`. + +| Key | Type | Required | Helm Template | Default | Description | +| :------------------------------------------- | :-----------: | :---------------------------: | :----------------: | :-----------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------- | +| probes | `dict` | ✅ | ❌ | `{}` | Define probes for the container | +| probes.liveness | `dict` | ✅ | ❌ | `{}` | Define the liveness probe | +| probes.readiness | `dict` | ✅ | ❌ | `{}` | Define the readiness probe | +| probes.startup | `dict` | ✅ | ❌ | `{}` | Define the startup probe | +| probes.[probe-name].enabled | `boolean` | ✅ | ❌ | `true` | Enable or disable the probe | +| probes.[probe-name].type | `string` | ❌ | ✅ | `http` | Define probe type (exec, http, https, tcp, grpc) (Used as a scheme in http(s) types) | +| probes.[probe-name].command | `list/string` | ✅ (On exec type) | ✅ | `""` | Define command(s). If it's single, can be defined as string (Only when exec type is used) | +| probes.[probe-name].port | `int` | ✅ (On grpc/tcp/http(s) type) | ✅ | `""` | Define the port, (Only when grpc/tcp/http/https type is used) | +| probes.[probe-name].path | `string` | ❌ | ✅ | `/` | Define the path (Only when https/http type is used) | +| probes.[probe-name].httpHeaders | `dict` | ❌ | ✅ (On value only) | `{}` | Define the httpHeaders in key-value pairs (Only when http/https type is used) | +| probes.[probe-name].spec.initialDelaySeconds | `int` | ❌ | ❌ | `{{ .Values.fallbackDefaults.probeTimeouts.[probe-name].initialDelaySeconds }}` | Define the initialDelaySeconds in seconds | +| probes.[probe-name].spec.periodSeconds | `int` | ❌ | ❌ | `{{ .Values.fallbackDefaults.probeTimeouts.[probe-name].periodSeconds }}` | Define the periodSeconds in seconds | +| probes.[probe-name].spec.timeoutSeconds | `int` | ❌ | ❌ | `{{ .Values.fallbackDefaults.probeTimeouts.[probe-name].timeoutSeconds }}` | Define the timeoutSeconds in seconds | +| probes.[probe-name].spec.failureThreshold | `int` | ❌ | ❌ | `{{ .Values.fallbackDefaults.probeTimeouts.[probe-name].failureThreshold }}` | Define the failureThreshold in seconds | +| probes.[probe-name].spec.successThreshold | `int` | ❌ | ❌ | `{{ .Values.fallbackDefaults.probeTimeouts.[probe-name].successThreshold }}` | Define the successThreshold in seconds (liveness and startup must always be 1) | + +--- + +Appears in: + +- `.Values.workload.[workload-name].podSpec.containers.[container-name].probes` + +--- + +Examples: + +```yaml +workload: + workload-name: + enabled: true + primary: true + podSpec: + containers: + container-name: + enabled: true + primary: true + probes: + liveness: + enabled: true + type: https + port: 8080 + path: /healthz + httpHeaders: + key1: value1 + key2: value2 + spec: + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 10 + failureThreshold: 10 + successThreshold: 10 + readiness: + enabled: true + type: tcp + port: 8080 + spec: + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 10 + failureThreshold: 10 + successThreshold: 10 + startup: + enabled: true + type: exec + command: + - command1 + - command2 + spec: + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 10 + failureThreshold: 10 + successThreshold: 10 +``` diff --git a/library/common/1.0.0/docs/container/resources.md b/library/common/1.0.0/docs/container/resources.md new file mode 100644 index 0000000000..87fb1ddbf0 --- /dev/null +++ b/library/common/1.0.0/docs/container/resources.md @@ -0,0 +1,51 @@ +# Resources + +Assume every key below has a prefix of `workload.[workload-name].podSpec.containers.[container-name]`. + +| Key | Type | Required | Helm Template | Default | Description | +| :------------------------ | :------: | :------: | :-----------: | :--------------------------------------------------------: | :------------------------------------------- | +| resources | `dict` | ✅ | ❌ | `{{ .Values.containerOptions.resources }}` | Define resources for the container | +| resources.requests | `dict` | ✅ | ❌ | `{{ .Values.containerOptions.resources.requests }}` | Define the requests for the container | +| resources.requests.cpu | `string` | ✅ | ❌ | `{{ .Values.containerOptions.resources.requests.cpu }}` | Define the requests.cpu for the container | +| resources.requests.memory | `string` | ✅ | ❌ | `{{ .Values.containerOptions.resources.requests.memory }}` | Define the requests.memory for the container | +| resources.limits | `dict` | ❌ | ❌ | `{{ .Values.containerOptions.resources.limits }}` | Define the limits for the container | +| resources.limits.cpu | `string` | ❌ | ❌ | `{{ .Values.containerOptions.resources.limits.cpu }}` | Define the limits.cpu for the container | +| resources.limits.memory | `string` | ❌ | ❌ | `{{ .Values.containerOptions.resources.limits.memory }}` | Define the limits.memory for the container | + +> Each value that is not defined in the `resources` under the container level, it will get replaced with the value defined `.Values.containerOptions.resources`. +> `requests` is **required**, because without it, kubernetes uses the `limits` as the `requests`. Which can lead pods to be evicted when they reach their `limits` or not even scheduled. +> `limits` is **optional**, can be set to "unlimited" by setting it's values (`cpu` and `memory`) to `0`. + +Regex Match: + +- [CPU Regex match](https://regex101.com/r/D4HouI/1) +- [Regex match](https://regex101.com/r/D4HouI/1 + +--- + +Appears in: + +- `.Values.workload.[workload-name].podSpec.containers.[container-name].resources` + +--- + +Examples: + +```yaml +workload: + workload-name: + enabled: true + primary: true + podSpec: + containers: + container-name: + enabled: true + primary: true + resources: + limits: + cpu: 1 + memory: 1Gi + requests: + cpu: 10m + memory: 50Mi +``` diff --git a/library/common/1.0.0/docs/container/securityContext.md b/library/common/1.0.0/docs/container/securityContext.md new file mode 100644 index 0000000000..ded58166b8 --- /dev/null +++ b/library/common/1.0.0/docs/container/securityContext.md @@ -0,0 +1,59 @@ +# Security Context + +Assume every key below has a prefix of `workload.[workload-name].podSpec.containers.[container-name]`. + +| Key | Type | Required | Helm Template | Default | Description | +| :--------------------------------------- | :-------: | :----------------------------: | :-----------: | :----------------------------------------------------------------: | :--------------------------------------------------------------------------------------- | +| securityContext | `dict` | ✅ | ❌ | `{{ .Values.securityContext.container }}` | Define securityContext for the container | +| securityContext.runAsUser | `int` | ✅ | ❌ | `{{ .Values.securityContext.container.runAsUser }}` | Define the runAsUser for the container | +| securityContext.runAsGroup | `int` | ✅ | ❌ | `{{ .Values.securityContext.container.runAsGroup }}` | Define the runAsGroup for the container | +| securityContext.readOnlyRootFilesystem | `boolean` | ✅ | ❌ | `{{ .Values.securityContext.container.readOnlyRootFilesystem }}` | Define the readOnlyRootFilesystem for the container | +| securityContext.allowPrivilegeEscalation | `boolean` | ✅ | ❌ | `{{ .Values.securityContext.container.allowPrivilegeEscalation }}` | Define the allowPrivilegeEscalation for the container | +| securityContext.privileged | `boolean` | ✅ | ❌ | `{{ .Values.securityContext.container.privileged }}` | Define the privileged for the container | +| securityContext.runAsNonRoot | `boolean` | ✅ | ❌ | `{{ .Values.securityContext.container.runAsNonRoot }}` | Define the runAsNonRoot for the container | +| securityContext.capabilities | `dict` | ✅ | ❌ | `{{ .Values.securityContext.container.capabilities }}` | Define the capabilities for the container | +| securityContext.capabilities.add | `list` | ✅ | ❌ | `{{ .Values.securityContext.container.capabilities.add }}` | Define the capabilities.add for the container | +| securityContext.capabilities.drop | `list` | ✅ | ❌ | `{{ .Values.securityContext.container.capabilities.drop }}` | Define the capabilities.drop for the container | +| securityContext.seccompProfile | `dict` | ✅ | ❌ | `{{ .Values.securityContext.container.seccompProfile }}` | Define the seccompProfile for the container | +| securityContext.seccompProfile.type | `string` | ✅ | ❌ | `{{ .Values.securityContext.container.seccompProfile.type }}` | Define the seccompProfile.type for the container (RuntimeDefault, Localhost, Unconfined) | +| securityContext.seccompProfile.profile | `string` | ✅ (Only when Localhost type ) | ❌ | `{{ .Values.securityContext.container.seccompProfile.profile }}` | Define the seccompProfile.profile for the container (Only when type is Localhost) | + +> Each value that is not defined in the `securityContext` under the container level, it will get replaced with the value defined `.Values.securityContext.container`. +> If a capability is defined in either `add` or `drop` on container level, it will **NOT** get merged +> with the value(s) from the `.Values.securityContext.container.capabilities.[add/drop]`. But it will override them. + +--- + +Appears in: + +- `.Values.workload.[workload-name].podSpec.containers.[container-name].securityContext` + +--- + +Examples: + +```yaml +workload: + workload-name: + enabled: true + primary: true + podSpec: + containers: + container-name: + enabled: true + primary: true + securityContext: + runAsNonRoot: true + runAsUser: 568 + runAsGroup: 568 + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + privileged: false + seccompProfile: + type: Localhost + profile: path/to/profile.json + capabilities: + add: [] + drop: + - ALL +``` diff --git a/library/common/1.0.0/docs/container/termination.md b/library/common/1.0.0/docs/container/termination.md new file mode 100644 index 0000000000..d1f05c6ba7 --- /dev/null +++ b/library/common/1.0.0/docs/container/termination.md @@ -0,0 +1,34 @@ +# Termination + +Assume every key below has a prefix of `workload.[workload-name].podSpec.containers.[container-name]`. + +| Key | Type | Required | Helm Template | Default | Description | +| :------------------------ | :------: | :------: | :-----------: | :-----: | :-------------------------------------------------- | +| termination | `dict` | ❌ | ❌ | `{}` | Define termination for the container | +| termination.messagePath | `string` | ❌ | ✅ | `""` | Define termination message path for the container | +| termination.messagePolicy | `string` | ❌ | ✅ | `""` | Define termination message policy for the container | + +--- + +Appears in: + +- `.Values.workload.[workload-name].podSpec.containers.[container-name].termination` + +--- + +Examples: + +```yaml +workload: + workload-name: + enabled: true + primary: true + podSpec: + containers: + container-name: + enabled: true + primary: true + termination: + messagePath: /dev/termination-log + messagePolicy: File +``` diff --git a/library/common/1.0.0/docs/imagePullSecret.md b/library/common/1.0.0/docs/imagePullSecret.md new file mode 100644 index 0000000000..c9f6a94468 --- /dev/null +++ b/library/common/1.0.0/docs/imagePullSecret.md @@ -0,0 +1,66 @@ +# Image Pull Secret + +| Key | Type | Required | Helm Template | Default | Description | +| :------------------------------------------------- | :-------: | :------: | :----------------: | :-----: | :---------------------------------------------- | +| imagePullSecret | `dict` | ❌ | ❌ | `{}` | Define the image pull secret as dicts | +| imagePullSecret.[pull-secret-name] | `dict` | ✅ | ❌ | `{}` | Holds configMap definition | +| imagePullSecret.[pull-secret-name].enabled | `boolean` | ✅ | ❌ | `false` | Enables or Disables the image pull secret | +| imagePullSecret.[pull-secret-name].labels | `dict` | ❌ | ✅ (On value only) | `{}` | Additional labels for image pull secret | +| imagePullSecret.[pull-secret-name].annotations | `dict` | ❌ | ✅ (On value only) | `{}` | Additional annotations for image pull secret | +| imagePullSecret.[pull-secret-name].data | `dict` | ✅ | ✅ | `{}` | Define the data of the image pull secret | +| imagePullSecret.[pull-secret-name].data.registry | `string` | ✅ | ✅ | `""` | Define the registry of the image pull secret | +| imagePullSecret.[pull-secret-name].data.username | `string` | ✅ | ✅ | `""` | Define the username of the image pull secret | +| imagePullSecret.[pull-secret-name].data.password | `string` | ✅ | ✅ | `""` | Define the password of the image pull secret | +| imagePullSecret.[pull-secret-name].data.email | `string` | ✅ | ✅ | `""` | Define the email of the image pull secret | +| imagePullSecret.[pull-secret-name].targetSelectAll | `boolean` | ❌ | ❌ | | Whether to assign the secret to all pods or not | +| imagePullSecret.[pull-secret-name].targetSelector | `list` | ❌ | ❌ | `[]` | Define the pod(s) to assign the secret | + +> When `targetSelectAll` is `true`, it will assign the secret to all pods (`targetSelector` is ignored in this case) +> When `targetSelector` is a list, each entry is a string with the pod name that will be assigned. Can have multiple entries +> When `targetSelector` is a empty, it will assign the secret to the primary pod + +--- + +Appears in: + +- `.Values.imagePullSecret` + +--- + +Naming scheme: + +- `$FullName-$ImagePullSecretName` (release-name-chart-name-imagePullSecretName) + +--- + +Examples: + +```yaml +imagePullSecret: + + pull-secret-name: + enabled: true + labels: + key: value + keytpl: "{{ .Values.some.value }}" + annotations: + key: value + keytpl: "{{ .Values.some.value }}" + data: + registry: quay.io + username: my_user + password: my_pass + email: my_mail@example.com + targetSelectAll: true + + other-pull-secret-name: + enabled: true + data: + registry: "{{ .Values.my_registry }}" + username: "{{ .Values.my_user }}" + password: "{{ .Values.my_pass }}" + email: "{{ .Values.my_mail }}" + targetSelector: + - workload-name1 + - workload-name2 +``` diff --git a/library/common/1.0.0/docs/notes.md b/library/common/1.0.0/docs/notes.md new file mode 100644 index 0000000000..4b8df84714 --- /dev/null +++ b/library/common/1.0.0/docs/notes.md @@ -0,0 +1,41 @@ +# Notes + +| Key | Type | Required | Helm Template | Default | Description | +| :----------- | :------: | :------: | :-----------: | :-------: | :------------------------------------------------------- | +| notes | `dict` | ❌ | ❌ | See below | Define values for NOTES.txt | +| notes.header | `string` | ❌ | ✅ | See below | Define header | +| notes.custom | `string` | ❌ | ✅ | See below | Define custom message, this go between header and footer | +| notes.footer | `string` | ❌ | ✅ | See below | Define footer | + +--- + +Appears in: + +- `.Values.notes` + +--- + +Default: + +```yaml +notes: + header: | + # Welcome to SCALE + Thank you for installing <{{ .Chart.Name }}>. + custom: "" + footer: | + # Documentation + Documentation for this chart can be found at ... + # Bug reports + If you find a bug in this chart, please file an issue at ... +``` + +--- + +Examples: + +```yaml +notes: + custom: | + This is a custom message +``` diff --git a/library/common/1.0.0/docs/persistence/README.md b/library/common/1.0.0/docs/persistence/README.md new file mode 100644 index 0000000000..284a703dd9 --- /dev/null +++ b/library/common/1.0.0/docs/persistence/README.md @@ -0,0 +1,97 @@ +# 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].type | `string` | ❌ | ❌ | `{{ .Values.fallbackDefaults.persistenceType }}` | Define the persistence type (ixVolume, hostPath, configmap, secret, device) | +| persistence.[volume-name].mountPath | `string` | ✅ | ✅ | `""` | Default mountPath for all containers that are selected | +| persistence.[volume-name].mountPropagation | `string` | ❌ | ✅ | `""` | Default mountPropagation for all containers that are selected | +| persistence.[volume-name].subPath | `string` | ❌ | ✅ | `""` | Default subPath for all containers that are selected | +| persistence.[volume-name].readOnly | `boolean` | ❌ | ❌ | `false` | Default readOnly for all containers that are selected | +| 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].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 | +| persistence.[volume-name].targetSelector.[pod-name].[container-name].mountPropagation | `string` | ❌ | ✅ | `[volume-name].mountPropagation` | Define the mountPropagation for the container | +| persistence.[volume-name].targetSelector.[pod-name].[container-name].subPath | `string` | ❌ | ✅ | `[volume-name].subPath` | Define the subPath for the container | +| persistence.[volume-name].targetSelector.[pod-name].[container-name].readOnly | `boolean` | ❌ | ❌ | `[volume-name].readOnly` | Define the readOnly for the container | + +> When `targetSelectAll` is `true`, it will define the volume to all pods and volumeMount to all containers (`targetSelector` is ignored in this case) +> When `targetSelector` is defined, it will define the volume to the pod(s) and volumeMount to the container(s) selected. See below for the selector structure. +> When `targetSelector` is a empty, it will define the volume to the primary pod and volumeMount to the primary container +> `targetSelectAll` is only useful when you want to mount a shared volume to all pods and containers. +> Otherwise, you should use `targetSelector` to define the volume to specific pods and containers. + +--- + +Appears in: + +- `.Values.persistence` + +--- + +Naming scheme: + +- `$FullName-$PersistenceName` (release-name-chart-name-PersistenceName) + +--- + +> Those are the common `keys` for all **persistence**. +> Additional keys, information and examples, see on the specific kind of persistence. + +- [configmap](configmap.md) +- [emptyDir](emptyDir.md) +- [ixVolume](ixVolume.md) +- [hostPath](hostPath.md) +- [device](device.md) +- [secret](secret.md) + +--- + +Examples: + +```yaml +# Example of a shared emptyDir volume +persistence: + shared: + enabled: true + type: emptyDir + mountPath: /shared + readOnly: false + targetSelectAll: true +``` + +```yaml +# Example of a volume mounted to a specific container with a specific mountPath +persistence: + config: + enabled: true + type: emptyDir + targetSelector: + my-pod: + my-container: {} + mountPath: /path + readOnly: false + my-other-container: {} + mountPath: /other/path + readOnly: false +``` + +```yaml +# Example of a volume mounted to a specific container using the default mountPath +persistence: + config: + enabled: true + type: emptyDir + mountPath: /path + readOnly: true + targetSelector: + my-pod: + my-container: {} + my-other-container: + mountPath: /other/path + readOnly: false +``` diff --git a/library/common/1.0.0/docs/persistence/configmap.md b/library/common/1.0.0/docs/persistence/configmap.md new file mode 100644 index 0000000000..99eca75276 --- /dev/null +++ b/library/common/1.0.0/docs/persistence/configmap.md @@ -0,0 +1,35 @@ +# configmap + +| Key | Type | Required | Helm Template | Default | Description | +| :----------------------------------------- | :-------: | :------: | :-----------: | :-----: | :------------------------------------------------------------------- | +| persistence.[volume-name].objectName | `string` | ✅ | ✅ | `""` | Define the configmap volume name | +| persistence.[volume-name].expandObjectName | `boolean` | ❌ | ❌ | `true` | Whether to expand (adding the fullname as prefix) the configmap name | +| persistence.[volume-name].defaultMode | `string` | ❌ | ✅ | `""` | Define the defaultMode (must be a string in format of "0777") | +| persistence.[volume-name].items | `list` | ❌ | ❌ | `[]` | Define a list of items for configmap | +| persistence.[volume-name].items.key | `string` | ✅ | ✅ | `""` | Define the key of the configmap | +| persistence.[volume-name].items.path | `string` | ✅ | ✅ | `""` | Define the path | + +--- + +Notes: + +View common `keys` of `persistence` in [persistence Documentation](README.md). + +--- + +Examples: + +```yaml +persistence: + configmap-vol: + enabled: true + type: configmap + objectName: configmap-name + expandObjectName: false + defaultMode: "0777" + items: + - key: key1 + path: path1 + - key: key2 + path: path2 +``` diff --git a/library/common/1.0.0/docs/persistence/device.md b/library/common/1.0.0/docs/persistence/device.md new file mode 100644 index 0000000000..bf9d07ae3e --- /dev/null +++ b/library/common/1.0.0/docs/persistence/device.md @@ -0,0 +1,28 @@ +# Device + +| Key | Type | Required | Helm Template | Default | Description | +| :------------------------------------- | :------: | :------: | :-----------: | :-----: | :---------------------- | +| persistence.[volume-name].hostPath | `string` | ✅ | ✅ | `""` | Define the hostPath | +| persistence.[volume-name].hostPathType | `string` | ❌ | ✅ | `""` | Define the hostPathType | + +> `device` type is pretty much the same as `hostPath`. The only difference is that if a `device` type is defined. +> We can take additional actions, like setting supplementalGroups to the container assigned, so it can utilize the device. + +--- + +Notes: + +View common `keys` of `persistence` in [persistence Documentation](README.md). + +--- + +Examples: + +```yaml +persistence: + dev-vol: + enabled: true + type: device + hostPath: /path/to/host + hostPathType: BlockDevice +``` diff --git a/library/common/1.0.0/docs/persistence/emptyDir.md b/library/common/1.0.0/docs/persistence/emptyDir.md new file mode 100644 index 0000000000..ce9c15c920 --- /dev/null +++ b/library/common/1.0.0/docs/persistence/emptyDir.md @@ -0,0 +1,25 @@ +# emptyDir + +| Key | Type | Required | Helm Template | Default | Description | +| :------------------------------- | :------: | :------: | :-----------: | :-----: | :----------------------------------------- | +| persistence.[volume-name].size | `string` | ❌ | ✅ | `""` | Define the sizeLimit of the emptyDir | +| persistence.[volume-name].medium | `string` | ❌ | ✅ | `""` | Define the medium of emptyDir (Memory, "") | + +--- + +Notes: + +View common `keys` of `persistence` in [persistence Documentation](README.md). + +--- + +Examples: + +```yaml +persistence: + emptyDir-vol: + enabled: true + type: emptyDir + medium: Memory + size: 2Gi +``` diff --git a/library/common/1.0.0/docs/persistence/hostPath.md b/library/common/1.0.0/docs/persistence/hostPath.md new file mode 100644 index 0000000000..2fbc3ce4f5 --- /dev/null +++ b/library/common/1.0.0/docs/persistence/hostPath.md @@ -0,0 +1,25 @@ +# hostPath + +| Key | Type | Required | Helm Template | Default | Description | +| :------------------------------------- | :------: | :------: | :-----------: | :-----: | :---------------------- | +| persistence.[volume-name].hostPath | `string` | ✅ | ✅ | `""` | Define the hostPath | +| persistence.[volume-name].hostPathType | `string` | ❌ | ✅ | `""` | Define the hostPathType | + +--- + +Notes: + +View common `keys` of `persistence` in [persistence Documentation](README.md). + +--- + +Examples: + +```yaml +persistence: + hostpath-vol: + enabled: true + type: hostPath + hostPath: /path/to/host + hostPathType: DirectoryOrCreate +``` diff --git a/library/common/1.0.0/docs/persistence/ixVolume.md b/library/common/1.0.0/docs/persistence/ixVolume.md new file mode 100644 index 0000000000..91df107b35 --- /dev/null +++ b/library/common/1.0.0/docs/persistence/ixVolume.md @@ -0,0 +1,25 @@ +# ixVolume + +| Key | Type | Required | Helm Template | Default | Description | +| :------------------------------------- | :------: | :------: | :-----------: | :-----: | :---------------------- | +| persistence.[volume-name].datasetName | `string` | ✅ | ✅ | `""` | Define the datasetName | +| persistence.[volume-name].hostPathType | `string` | ❌ | ✅ | `""` | Define the hostPathType | + +--- + +Notes: + +View common `keys` of `persistence` in [persistence Documentation](README.md). + +--- + +Examples: + +```yaml +persistence: + ix-vol: + enabled: true + type: ixVolume + datasetName: ix-app + hostPathType: DirectoryOrCreate +``` diff --git a/library/common/1.0.0/docs/persistence/secret.md b/library/common/1.0.0/docs/persistence/secret.md new file mode 100644 index 0000000000..642c081504 --- /dev/null +++ b/library/common/1.0.0/docs/persistence/secret.md @@ -0,0 +1,35 @@ +# secret + +| Key | Type | Required | Helm Template | Default | Description | +| :----------------------------------------- | :-------: | :------: | :-----------: | :-----: | :------------------------------------------------------------------- | +| persistence.[volume-name].objectName | `string` | ✅ | ✅ | `""` | Define the secret volume name | +| persistence.[volume-name].expandObjectName | `boolean` | ❌ | ❌ | `true` | Whether to expand (adding the fullname as prefix) the secret name | +| persistence.[volume-name].defaultMode | `string` | ❌ | ✅ | `""` | Define the defaultMode (must be a string in format of "0777") | +| persistence.[volume-name].items | `list` | ❌ | ❌ | `[]` | Define a list of items for secret | +| persistence.[volume-name].items.key | `string` | ✅ | ✅ | `""` | Define the key of the secret | +| persistence.[volume-name].items.path | `string` | ✅ | ✅ | `""` | Define the path | + +--- + +Notes: + +View common `keys` of `persistence` in [persistence Documentation](README.md). + +--- + +Examples: + +```yaml +persistence: + secret-vol: + enabled: true + type: secret + objectName: secret-name + expandObjectName: false + defaultMode: "0777" + items: + - key: key1 + path: path1 + - key: key2 + path: path2 +``` diff --git a/library/common/1.0.0/docs/rbac.md b/library/common/1.0.0/docs/rbac.md new file mode 100644 index 0000000000..1d0a9ff9eb --- /dev/null +++ b/library/common/1.0.0/docs/rbac.md @@ -0,0 +1,91 @@ +# RBAC + +| Key | Type | Required | Helm Template | Default | Description | +| :--------------------------------------- | :-------: | :------: | :----------------: | :-----: | :------------------------------------------------------------------------ | +| rbac | `dict` | ❌ | ❌ | `{}` | Define the rbac as dicts | +| rbac.[rbac-name] | `dict` | ✅ | ❌ | `{}` | Holds rbac definition | +| rbac.[rbac-name].enabled | `boolean` | ✅ | ❌ | `false` | Enables or Disables the rbac | +| rbac.[rbac-name].primary | `boolean` | ❌ | ❌ | `false` | Sets the rbac as primary | +| rbac.[rbac-name].clusterWide | `boolean` | ❌ | ❌ | `false` | Sets the rbac as cluster wide (ClusterRole, ClusterRoleBinding) | +| rbac.[rbac-name].labels | `dict` | ❌ | ✅ (On value only) | `{}` | Additional labels for rbac | +| rbac.[rbac-name].annotations | `dict` | ❌ | ✅ (On value only) | `{}` | Additional annotations for rbac | +| rbac.[rbac-name].allServiceAccounts | `boolean` | ❌ | ❌ | | Whether to assign all service accounts or not to the (Cluster)RoleBinding | +| rbac.[rbac-name].serviceAccounts | `list` | ❌ | ❌ | `[]` | Define the service account(s) to assign the (Cluster)RoleBinding | +| rbac.[rbac-name].rules | `list` | ✅ | ❌ | `[]` | Define the `rules` for the (Cluster)Role | +| rbac.[rbac-name].rules.apiGroups | `list` | ✅ | ❌ | `[]` | Define the `apiGroups` list for the `rules` for the (Cluster)Role | +| rbac.[rbac-name].rules.apiGroups.[entry] | `string` | ✅ | ✅ | | Entry of the `apiGroups` | +| rbac.[rbac-name].rules.resources | `list` | ✅ | ❌ | `[]` | Define the `resources` list for the `rules` for the (Cluster)Role | +| rbac.[rbac-name].rules.resources.[entry] | `string` | ✅ | ✅ | | Entry of the `resources` | +| rbac.[rbac-name].rules.verbs | `list` | ✅ | ❌ | `[]` | Define the `verbs` list for the `rules` for the (Cluster)Role | +| rbac.[rbac-name].rules.verbs.[entry] | `string` | ✅ | ✅ | | Entry of the `verbs` | +| rbac.[rbac-name].subjects | `list` | ❌ | ❌ | `[]` | Define `subjects` for (Cluster)RoleBinding | +| rbac.[rbac-name].subjects.kind | `string` | ✅ | ✅ | `""` | Define the `kind` of `subjects` entry | +| rbac.[rbac-name].subjects.name | `string` | ✅ | ✅ | `""` | Define the `name` of `subjects` entry | +| rbac.[rbac-name].subjects.apiGroup | `string` | ✅ | ✅ | `""` | Define the `apiGroup` of `subjects` entry | + +> When `allServiceAccounts` is `true`, it will assign the all the serviceAccount(s) to the (Cluster)RoleBinding (`serviceAccounts` is ignored in this case) +> When `serviceAccounts` is a list, each entry is a string with the serviceAccount name that will be assigned to the (Cluster)RoleBinding. Can have multiple entries. +> When `serviceAccounts` is a empty, it will assign the primary serviceAccount to the primary rbac + +--- + +Appears in: + +- `.Values.rbac` + +--- + +Naming scheme: + +- Primary: `$FullName` (release-name-chart-name) +- Non-Primary: `$FullName-$RBACName` (release-name-chart-name-RBACName) + +--- + +Examples: + +```yaml +rbac: + rbac-name: + enabled: true + primary: true + clusterWide: true + labels: + key: value + keytpl: "{{ .Values.some.value }}" + annotations: + key: value + keytpl: "{{ .Values.some.value }}" + allServiceAccounts: true + rules: + - apiGroups: + - "" + resources: + - "{{ .Values.some.value }}" + verbs: + - get + - "{{ .Values.some.value }}" + - watch + subjects: + - kind: my-kind + name: "{{ .Values.some.value }}" + apiGroup: my-api-group + + other-rbac-name: + enabled: true + serviceAccounts: + - service-account-name + rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch + subjects: + - kind: my-kind + name: my-name + apiGroup: my-api-group +``` diff --git a/library/common/1.0.0/docs/scaleCertificate.md b/library/common/1.0.0/docs/scaleCertificate.md new file mode 100644 index 0000000000..392698e526 --- /dev/null +++ b/library/common/1.0.0/docs/scaleCertificate.md @@ -0,0 +1,85 @@ +# Scale Certificate + +| Key | Type | Required | Helm Template | Default | Description | +| :--------------------------------------- | :-------: | :------: | :----------------: | :-----: | :-------------------------------------------- | +| scaleCertificate | `dict` | ❌ | ❌ | `{}` | Define the certificate as dicts | +| scaleCertificate.[cert-name].enabled | `boolean` | ✅ | ❌ | `false` | Enables the certificate (The secret creation) | +| scaleCertificate.[cert-name].labels | `dict` | ❌ | ✅ (On value only) | `{}` | Additional labels for secret | +| scaleCertificate.[cert-name].annotations | `dict` | ❌ | ✅ (On value only) | `{}` | Additional annotations for secret | +| scaleCertificate.[cert-name].id | `string` | ✅ | ❌ | `""` | ID of the certificate in ixCertificates | + +> A secret will be created with 2 keys in the data section: `tls.crt` and `tls.key`. + +--- + +Appears in: + +- `.Values.scaleCertificate` + +--- + +Naming scheme: + +- `$FullName-$CertName` (release-name-chart-name-CertName) + +--- + +Examples: + +```yaml +scaleCertificate: + cert-name: + enabled: false + labels: {} + annotations: {} + id: 1 +``` + +You can mount certificate as a secret using the following snippet: + +```yaml +scaleCertificate: + cert-name: + enabled: false + id: 1 + +persistence: + # This will mount it on the primary pod/container + cert-vol: + enabled: true + type: secret + objectName: cert-name + expandObjectName: true # You can omit this, it's the default + # subPath + mountPath: /path/to/mount/cert.crt + subPath: cert.crt + # or items + mountPath: /path/to/mount + items: + - key: tls.crt + path: cert.crt + + # This will mount it on the specific pod/container + cert-vol: + enabled: true + type: secret + objectName: cert-name + expandObjectName: true # You can omit this, it's the default + # subPath + subPath: tls.crt + targetSelector: + workload-name: + container-name: + mountPath: /path/to/mount/cert.crt + # subPath: cert.crt (You can define subPath here as well, per container) + # or items + items: + - key: tls.crt + path: cert.crt + targetSelector: + workload-name: + container-name: + mountPath: /path/to/mount + +# Both will result in a mounted file in the container at /path/to/mount/cert.crt +``` diff --git a/library/common/1.0.0/docs/scaleExternalInterface.md b/library/common/1.0.0/docs/scaleExternalInterface.md new file mode 100644 index 0000000000..0da16497dc --- /dev/null +++ b/library/common/1.0.0/docs/scaleExternalInterface.md @@ -0,0 +1,47 @@ +# Scale External Interface + +| Key | Type | Required | Helm Template | Default | Description | +| :------------------------------------------------ | :-------: | :-----------------------------: | :-----------: | :-----: | :------------------------------------------------------------------------------ | +| scaleExternalInterface | `list` | ❌ | ❌ | `[]` | Define the external interfaces as list | +| scaleExternalInterface.targetSelectAll | `boolean` | ❌ | ❌ | `false` | Whether to add the annotation for this external interface to all workloads | +| scaleExternalInterface.targetSelector | `list` | ❌ | ❌ | `[]` | Which workloads to add the annotations | +| scaleExternalInterface.hostInterface | `string` | ✅ | ❌ | `""` | Define the hostInterface, (options in GUI populated from Middleware references) | +| scaleExternalInterface.ipam | `dict` | ✅ | ❌ | `{}` | Define the ipam | +| scaleExternalInterface.ipam.type | `string` | ✅ | ❌ | `""` | Define the ipam type (dchp, static) | +| scaleExternalInterface.staticIPConfiguration | `list` | ✅ (Only when static ipam type) | ❌ | `[]` | Define static IP Configuration (Only with static ipam type) | +| scaleExternalInterface.staticIPConfiguration.[IP] | `string` | ✅ | ❌ | `""` | Define the static IP (Only with static ipam type) | +| scaleExternalInterface.staticRoutes | `list` | ❌ | ❌ | `[]` | Define static routes (Only with static ipam type) | +| scaleExternalInterface.staticRoutes.destination | `string` | ✅ | ❌ | `""` | Define the static destination (Only with static ipam type) | +| scaleExternalInterface.staticRoutes.gateway | `string` | ✅ | ❌ | `""` | Define the static gateway (Only with static ipam type) | + +> When `targetSelectAll` is `true`, it will add the annotations to all pods (`targetSelector` is ignored in this case) +> When `targetSelector` is a list, each entry is a string, with the pod name that will add the annotations. Can have multiple entries. +> When `targetSelector` is a empty, it will add the annotations to the primary pod + +--- + +Appears in: + +- `.Values.scaleExternalInterface` + +--- + +Naming scheme: + +- `ix-$ReleaseName-$index` (ix-release-name-0) + +--- + +Examples: + +```yaml +scaleExternalInterface: + - hostInterface: "" + ipam: + type: "" + staticRoutes: [] + staticIPConfigurations: [] + # targetSelectAll: false + targetSelector: + - workload-name +``` diff --git a/library/common/1.0.0/docs/scaleGPU.md b/library/common/1.0.0/docs/scaleGPU.md new file mode 100644 index 0000000000..d3c77a9cb8 --- /dev/null +++ b/library/common/1.0.0/docs/scaleGPU.md @@ -0,0 +1,33 @@ +# Scale GPU + +| Key | Type | Required | Helm Template | Default | Description | +| :-------------------------------------------------- | :------: | :------: | :-----------: | :-----: | :-------------------------------------------- | +| scaleGPU | `list` | ❌ | ❌ | `[]` | Define the external interfaces as list | +| scaleGPU.targetSelector | `dict` | ❌ | ❌ | `{}` | Where to assign the GPU | +| scaleGPU.targetSelector.[pod-name] | `list` | ❌ | ❌ | `[]` | The workload to select | +| scaleGPU.targetSelector.[pod-name].[container-name] | `string` | ✅ | ❌ | `""` | The container to select | +| scaleGPU.gpu | `dict` | ✅ | ❌ | `{}` | The GPU key value pair to define in resources | + +> When `targetSelector` is a dict, each entry is a list, containing the name(s) of the container(s) to assign the GPU +> When `targetSelector` is a empty, it will assign the GPU to the primary pod/container +> Selected pod's will get appended the group `44` in `supplementalGroups`. This is to allow rootless containers to access the GPU + +--- + +Appears in: + +- `.Values.scaleGPU` + +--- + +Examples: + +```yaml +scaleGPU: + - gpu: + # Injected from SCALE UI/Middleware using $ref + nvidia.com/gpu: "1" + targetSelector: + workload-name: + - container-name +``` diff --git a/library/common/1.0.0/docs/secret.md b/library/common/1.0.0/docs/secret.md new file mode 100644 index 0000000000..14e0f0ea58 --- /dev/null +++ b/library/common/1.0.0/docs/secret.md @@ -0,0 +1,50 @@ +# Secret + +| Key | Type | Required | Helm Template | Default | Description | +| :------------------------------- | :-------: | :------: | :----------------: | :------: | :-------------------------------- | +| secret | `dict` | ❌ | ❌ | `{}` | Define the secret as dicts | +| secret.[secret-name] | `dict` | ✅ | ❌ | `{}` | Holds secret definition | +| secret.[secret-name].enabled | `boolean` | ✅ | ❌ | `false` | Enables or Disables the secret | +| secret.[secret-name].labels | `dict` | ❌ | ✅ (On value only) | `{}` | Additional labels for secret | +| secret.[secret-name].annotations | `dict` | ❌ | ✅ (On value only) | `{}` | Additional annotations for secret | +| secret.[secret-name].type | `string` | ❌ | ✅ | `Opaque` | Custom secret type | +| secret.[secret-name].data | `dict` | ✅ | ✅ | `{}` | Define the data of the secret | + +--- + +Appears in: + +- `.Values.secret` + +--- + +Naming scheme: + +- `$FullName-$SecretName` (release-name-chart-name-SecretName) + +--- + +Examples: + +```yaml +secret: + + secret-name: + enabled: true + type: CustomSecretType + labels: + key: value + keytpl: "{{ .Values.some.value }}" + annotations: + key: value + keytpl: "{{ .Values.some.value }}" + data: + key: value + + other-secret-name: + enabled: true + data: + key: | + multi line + text value +``` diff --git a/library/common/1.0.0/docs/service/ClusterIP.md b/library/common/1.0.0/docs/service/ClusterIP.md new file mode 100644 index 0000000000..7fbaa8909f --- /dev/null +++ b/library/common/1.0.0/docs/service/ClusterIP.md @@ -0,0 +1,50 @@ +# ClusterIP + +| Key | Type | Required | Helm Template | Default | Description | +| :------------------------------------------------------------------- | :-------: | :------: | :-----------: | :-----: | :------------------------------------------------------------------------- | +| service.[service-name].clusterIP | `string` | ❌ | ✅ | | Custom Cluster IP | +| service.[service-name].ipFamilyPolicy | `string` | ❌ | ✅ | | Define the ipFamilyPolicy (SingleStack, PreferDualStack, RequireDualStack) | +| service.[service-name].ipFamilies | `list` | ❌ | ❌ | | Define the ipFamilies | +| service.[service-name].ipFamilies.[ipFamily] | `string` | ✅ | ✅ | | Define the ipFamily (IPv4, IPv6) | +| service.[service-name].sessionAffinity | `string` | ❌ | ✅ | | Define the session affinity (ClientIP, None) | +| service.[service-name].sessionAffinityConfig.clientIP.timeoutSeconds | `int` | ❌ | ✅ | | Define the timeout for ClientIP session affinity (0-86400) | +| service.[service-name].externalIPs | `list` | ❌ | ❌ | | Define externalIPs | +| service.[service-name].externalIPs.[externalIP] | `string` | ✅ | ✅ | | The external IP | + +--- + +Notes: + +View common `keys` of `service` in [service Documentation](README.md). + +--- + +Examples: + +```yaml +service: + service-clusterip: + enabled: true + primary: true + publishNotReadyAddresses: true + clusterIP: 172.16.20.233 + publishNotReadyAddresses: true + ipFamilyPolicy: SingleStack + ipFamilies: + - IPv4 + externalIPs: + - 10.200.230.34 + sessionAffinity: ClientIP + sessionAffinityConfig: + clientIP: + timeoutSeconds: 86400 + targetSelector: pod-name + ports: + port-name: + enabled: true + primary: true + targetSelector: container-name + port: 80 + protocol: http + targetPort: 8080 +``` diff --git a/library/common/1.0.0/docs/service/NodePort.md b/library/common/1.0.0/docs/service/NodePort.md new file mode 100644 index 0000000000..630ceabbe4 --- /dev/null +++ b/library/common/1.0.0/docs/service/NodePort.md @@ -0,0 +1,51 @@ +# NodePort + +| Key | Type | Required | Helm Template | Default | Description | +| :------------------------------------------------------------------- | :-------: | :------: | :-----------: | :-----: | :------------------------------------------------------------------------- | +| service.[service-name].clusterIP | `string` | ❌ | ✅ | | Custom Cluster IP | +| service.[service-name].ipFamilyPolicy | `string` | ❌ | ✅ | | Define the ipFamilyPolicy (SingleStack, PreferDualStack, RequireDualStack) | +| service.[service-name].ipFamilies | `list` | ❌ | ❌ | | Define the ipFamilies | +| service.[service-name].ipFamilies.[ipFamily] | `string` | ✅ | ✅ | | Define the ipFamily (IPv4, IPv6) | +| service.[service-name].sessionAffinity | `string` | ❌ | ✅ | | Define the session affinity (ClientIP, None) | +| service.[service-name].sessionAffinityConfig.clientIP.timeoutSeconds | `int` | ❌ | ✅ | | Define the timeout for ClientIP session affinity (0-86400) | +| service.[service-name].externalIPs | `list` | ❌ | ❌ | | Define externalIPs | +| service.[service-name].externalIPs.[externalIP] | `string` | ✅ | ✅ | | The external IP | +| service.[service-name].externalTrafficPolicy | `string` | ❌ | ✅ | | Define the external traffic policy (Cluster, Local) | +| service.[service-name].ports.[port-name].nodePort | `int` | ✅ | ✅ | | Define the node port that will be exposed on the node | + +--- + +Notes: + +View common `keys` of `service` in [service Documentation](README.md). + +--- + +Examples: + +```yaml +service: + service-nodeport: + enabled: true + primary: true + type: NodePort + clusterIP: 172.16.20.233 + publishNotReadyAddresses: true + externalIPs: + - 10.200.230.34 + sessionAffinity: ClientIP + sessionAffinityConfig: + clientIP: + timeoutSeconds: 86400 + externalTrafficPolicy: Cluster + targetSelector: pod-name + ports: + port-name: + enabled: true + primary: true + targetSelector: container-name + port: 80 + protocol: http + targetPort: 8080 + nodePort: 30080 +``` diff --git a/library/common/1.0.0/docs/service/README.md b/library/common/1.0.0/docs/service/README.md new file mode 100644 index 0000000000..7c5e7eb24e --- /dev/null +++ b/library/common/1.0.0/docs/service/README.md @@ -0,0 +1,43 @@ +# Service + +| Key | Type | Required | Helm Template | Default | Description | +| :------------------------------------------------------ | :-------: | :------: | :----------------: | :----------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------ | +| service | `dict` | ❌ | ❌ | `{}` | Define the service as dicts | +| service.[service-name] | `dict` | ✅ | ❌ | `{}` | Holds service definition | +| service.[service-name].enabled | `boolean` | ✅ | ❌ | `false` | Enables or Disables the service | +| service.[service-name].labels | `dict` | ❌ | ✅ (On value only) | `{}` | Additional labels for service | +| service.[service-name].annotations | `dict` | ❌ | ✅ (On value only) | `{}` | Additional annotations for service | +| service.[service-name].type | `string` | ❌ | ✅ | `ClusterIP` | Define the service type (ClusterIP, NodePort) | +| service.[service-name].publishNotReadyAddresses | `boolean` | ❌ | ❌ | `false` | Define whether to publishNotReadyAddresses or not | +| service.[service-name].targetSelector | `string` | ❌ | ❌ | `""` | Define the pod to link the service, by default will use the primary pod | +| service.[service-name].ports | `list` | ✅ | ❌ | `{}` | Define the ports of the service | +| service.[service-name].ports.[port-name] | `dict` | ✅ | ❌ | `{}` | Define the port dict | +| service.[service-name].ports.[port-name].port | `int` | ✅ | ✅ | | Define the port that will be exposed by the service | +| service.[service-name].ports.[port-name].targetPort | `int` | ❌ | ✅ | `[port-name].port` | Define the target port (No named ports, as this will be used to assign the containerPort to containers) | +| service.[service-name].ports.[port-name].protocol | `string` | ❌ | ✅ | `{{ .Values.fallbackDefaults.serviceProtocol }}` | Define the port protocol (http, https, tcp, udp). (Also used by the container ports and probes, http and https are converted to tcp where needed) | +| service.[service-name].ports.[port-name].nodePort | `string` | ❌ | ✅ | | Define the node port | +| service.[service-name].ports.[port-name].hostPort | `string` | ❌ | ❌ | | Define the hostPort, should be **avoided**, unless **ABSOLUTELY** necessary | +| service.[service-name].ports.[port-name].targetSelector | `string` | ❌ | ❌ | | Define the container to link this port (Must be on under the pod linked above) | + +> When `targetSelector`(s) is empty, it will define auto-select the primary pod/container + +--- + +Appears in: + +- `.Values.service` + +--- + +Naming scheme: + +- Primary: `$FullName` (release-name-chart-name) +- Non-Primary: `$FullName-$ServiceName` (release-name-chart-name-ServiceName) + +--- + +> Those are the common `keys` for all **service**. +> Additional keys, information and examples, see on the specific kind of service. + +- [ClusterIP](ClusterIP.md) +- [NodePort](NodePort.md) diff --git a/library/common/1.0.0/docs/serviceAccount.md b/library/common/1.0.0/docs/serviceAccount.md new file mode 100644 index 0000000000..fffbbc07dc --- /dev/null +++ b/library/common/1.0.0/docs/serviceAccount.md @@ -0,0 +1,60 @@ +# Service Account + +| Key | Type | Required | Helm Template | Default | Description | +| :--------------------------------------- | :-------: | :------: | :----------------: | :-----: | :------------------------------------------------------ | +| serviceAccount | `dict` | ❌ | ❌ | `{}` | Define the serviceAccount as dicts | +| serviceAccount.[sa-name] | `dict` | ✅ | ❌ | `{}` | Holds service account definition | +| serviceAccount.[sa-name].enabled | `boolean` | ✅ | ❌ | `false` | Enables or Disables the service account | +| serviceAccount.[sa-name].primary | `boolean` | ❌ | ❌ | `false` | Sets the service account as primary | +| serviceAccount.[sa-name].labels | `dict` | ❌ | ✅ (On value only) | `{}` | Additional labels for service account | +| serviceAccount.[sa-name].annotations | `dict` | ❌ | ✅ (On value only) | `{}` | Additional annotations for service account | +| serviceAccount.[sa-name].targetSelectAll | `boolean` | ❌ | ❌ | | Whether to assign the serviceAccount to all pods or not | +| serviceAccount.[sa-name].targetSelector | `list` | ❌ | ❌ | `[]` | Define the pod(s) to assign the serviceAccount | + +> When `targetSelectAll` is `true`, it will assign the serviceAccount to all pods (`targetSelector` is ignored in this case) +> When `targetSelector` is a list, each entry is a string, with the pod name that will be assigned. Can have multiple entries. +> When `targetSelector` is a empty, it will assign the serviceAccount to the primary pod + +--- + +Appears in: + +- `.Values.serviceAccount` + +--- + +Naming scheme: + +- Primary: `$FullName` (release-name-chart-name) +- Non-Primary: `$FullName-$ServiceAccountName` (release-name-chart-name-ServiceAccountName) + +--- + +Notes: + +By default the `automountServiceAccountToken` is set to `false` for all service accounts. +You have to explicitly set it to `true` on per pod(workload) basis with `workload.[workload-name].podSpec.automountServiceAccountToken` + +--- + +Examples: + +```yaml +serviceAccount: + sa-name: + enabled: true + primary: true + labels: + key: value + keytpl: "{{ .Values.some.value }}" + annotations: + key: value + keytpl: "{{ .Values.some.value }}" + targetSelectAll: true + + other-sa-name: + enabled: true + targetSelector: + - pod-name + - other-pod-name +``` diff --git a/library/common/1.0.0/docs/workload/README.md b/library/common/1.0.0/docs/workload/README.md new file mode 100644 index 0000000000..3b6956dae6 --- /dev/null +++ b/library/common/1.0.0/docs/workload/README.md @@ -0,0 +1,137 @@ +# workload + +| Key | Type | Required | Helm Template | Default | Description | +| :------------------------------------------------------------------- | :-------: | :------: | :----------------: | :-------------------------------------------------------------: | :--------------------------------------------------------------------------------- | +| workload | `dict` | ❌ | ❌ | `{}` | Define the workload as dicts | +| workload.[workload-name] | `dict` | ✅ | ❌ | `{}` | Holds workload definition | +| workload.[workload-name].enabled | `boolean` | ✅ | ❌ | `false` | Enables or Disables the workload | +| workload.[workload-name].primary | `boolean` | ✅ | ❌ | `false` | Sets the workload as primary | +| workload.[workload-name].labels | `dict` | ❌ | ✅ (On value only) | `{}` | Additional labels for workload | +| workload.[workload-name].annotations | `dict` | ❌ | ✅ (On value only) | `{}` | Additional annotations for workload | +| workload.[workload-name].type | `string` | ✅ | ❌ | `""` | Define the kind of the workload (Deployment, CronJob, Job) | +| workload.[workload-name].podSpec | `dict` | ✅ | ❌ | `{}` | Holds the pod definition | +| workload.[workload-name].podSpec.labels | `dict` | ❌ | ✅ (On value only) | `{}` | Additional Pod Labels | +| workload.[workload-name].podSpec.annotations | `dict` | ❌ | ✅ (On value only) | `{}` | Pod Annotations | +| workload.[workload-name].podSpec.automountServiceAccountToken | `boolean` | ❌ | ❌ | `{{ .Values.podOptions.automountServiceAccoutnToken }}` (false) | Pod's automountServiceAccountToken | +| workload.[workload-name].podSpec.hostNetwork | `boolean` | ❌ | ❌ | `{{ .Values.podOptions.hostNetwork }}` (false) | Pod's hostNetwork | +| workload.[workload-name].podSpec.enableServiceLinks | `boolean` | ❌ | ❌ | `{{ .Values.podOptions.enableServiceLinks }}` (false) | Pod's enableServiceLinks | +| workload.[workload-name].podSpec.restartPolicy | `string` | ❌ | ✅ | `{{ .Values.podOptions.restartPolicy }}` (Always) | Pod's restartPolicy. (Always, Never, OnFailure) | +| workload.[workload-name].podSpec.hostname | `string` | ❌ | ✅ | `""` | Pod's hostname | +| workload.[workload-name].podSpec.terminationGracePeriodSeconds | `int` | ❌ | ✅ | `{{ .Values.podOptions.terminationGracePeriodSeconds }}` (120) | Pod's terminationGracePeriodSeconds | +| workload.[workload-name].podSpec.hostAliases | `list` | ❌ | ❌ | | Pod's host aliases | +| workload.[workload-name].podSpec.hostAliases.ip | `string` | ❌ | ✅ | | Value for `ip` in hosts aliases | +| workload.[workload-name].podSpec.hostAliases.hostnames | `list` | ❌ | ❌ | | Hostnames for the `ip` in hosts aliases | +| workload.[workload-name].podSpec.hostAliases.hostnames.[host-name] | `string` | ❌ | ✅ | | [Value] for `hostnames` for the `ip` in hosts aliases | +| workload.[workload-name].podSpec.dnsPolicy | `string` | ❌ | ✅ | `{{ .Values.podOptions.dnsPolicy }}` (ClusterFirst) | Pod's DNS Policy (ClusterFirst, ClusterFirstWithHostNet, Default, None). | +| workload.[workload-name].podSpec.tolerations | `list` | ❌ | ❌ | `{{ .Values.podOptions.tolerations }}` ([]) | Pod's Tolerations | +| workload.[workload-name].podSpec.tolerations.operator | `string` | ✅ | ✅ | | Toleration's `operator` (Equal, Exists) | +| workload.[workload-name].podSpec.tolerations.key | `string` | ❌/✅ | ✅ | | Toleration's `key`. Required only when `operator` = `Equal` | +| workload.[workload-name].podSpec.tolerations.value | `string` | ❌/✅ | ✅ | | Toleration's `value`. Required only when `operator` = `Equal` | +| workload.[workload-name].podSpec.tolerations.effect | `string` | ❌ | ✅ | | Toleration's `effect`.(NoExecute, NoSchedule, PreferNoSchedule) | +| workload.[workload-name].podSpec.tolerations.tolerationSeconds | `int` | ❌ | ❌ | | Toleration's `tolerationSeconds`. | +| workload.[workload-name].podSpec.runtimeClassName | `string` | ❌ | ✅ | `{{ .Values.podOptions.runtimeClassName }}` ("") | Pod's runtimeClassName | +| workload.[workload-name].podSpec.securityContext | `dict` | ❌ | ❌ | `{{ .Values.securityContext.pod }}` | Pod's securityContext | +| workload.[workload-name].podSpec.securityContext.fsGroup | `int` | ❌ | ❌ | `568` | Pod's fsGroup | +| workload.[workload-name].podSpec.securityContext.fsGroupChangePolicy | `string` | ❌ | ❌ | `OnRootMismatch` | Pod's fsGroupChangePolicy (Always, OnRootMismatch) | +| workload.[workload-name].podSpec.securityContext.supplementalGroups | `list` | ❌ | ❌ | `[]` | Pod's supplementalGroups (list of `int`) | +| workload.[workload-name].podSpec.securityContext.sysctls | `list` | ❌ | ❌ | `[]` | Pod's sysctls | +| workload.[workload-name].podSpec.securityContext.sysctls.name | `string` | ✅ | ✅ | `""` | `name` of the sysctl | +| workload.[workload-name].podSpec.securityContext.sysctls.value | `string` | ✅ | ✅ | `""` | `value` of the sysctl | +| workload.[workload-name].podSpec.containers | `dict` | ❌ | ❌ | `{}` | Define container(s) | +| workload.[workload-name].podSpec.initContainers | `dict` | ❌ | ❌ | `{}` | Define initContainer(s) | + +--- + +Notes + +> `dnsPolicy` is set automatically to `ClusterFirstWithHostNet` when `hostNetwork` is `true` > `runtimeClassName` will ignore any value set and use the `.Values.global.ixChartContext.nvidiaRuntimeClassName`, +> if a GPU is assigned to a container and Scale Middleware sets `.Values.global.ixChartContext.addNvidiaRuntimeClass` to `true`. +> Note that it will only set the `runtimeClassName` on the pod that this container belongs to. +> **sysctl** `net.ipv4.ip_unprivileged_port_start` will be automatically set to the lowest `targetPort` (or `port` if targetPort is not defined) number assigned to the pod. +> **sysctl** `net.ipv4.ping_group_range` will be automatically set to the lowest and highest `targetPort` (or `port` if targetPort is not defined) number assigned to the pod. + +--- + +Appears in: + +- `.Values.workload` + +--- + +Naming scheme: + +- Primary: `$FullName` (release-name-chart-name) +- Non-Primary: `$FullName-$WorkloadName` (release-name-chart-name-workload-name) + +--- + +> Those are the common `keys` for all **workloads**. +> Additional keys, information and examples, see on the specific kind of workload + +- [Deployment](deployment.md) +- [CronJob](cronjob.md) +- [Job](job.md) + +> Additional keys, information and examples for `workload.[workload-name].podSpec.containers`. + +- [Container](../container/README.md) +- [InitContainer](../container/README.md#initcontainer) + +--- + +Examples: + +```yaml +workload: + workload-name: + enabled: true + primary: true + labels: + key: value + annotations: + key: value + podSpec: + labels: + key: value + annotations: + key: value + automountServiceAccountToken: true + hostNetwork: false + enableServiceLinks: false + hostname: some-hostname + terminationGracePeriodSeconds: 100 + hostAliases: + - ip: 10.10.10.100 + hostnames: + - myserver.local + - storage.local + - ip: 10.10.10.101 + hostnames: + - myotherserver.local + - backups.local + dnsPolicy: ClusterFirst + dnsConfig: + nameservers: + - 1.1.1.1 + - 1.0.0.1 + searches: + - ns1.svc.cluster-domain.example + - my.dns.search.suffix + options: + - name: ndots + value: "2" + - name: edns0 + tolerations: + - operator: Exists + effect: NoExecute + tolerationSeconds: 3600 + runtimeClassName: some-runtime-class + securityContext: + fsGroup: 568 + fsGroupChangePolicy: OnRootMismatch + supplementalGroups: + - 568 + sysctls: + - name: net.ipv4.ip_local_port_range + value: 1024 65535 +``` diff --git a/library/common/1.0.0/docs/workload/cronjob.md b/library/common/1.0.0/docs/workload/cronjob.md new file mode 100644 index 0000000000..b192161175 --- /dev/null +++ b/library/common/1.0.0/docs/workload/cronjob.md @@ -0,0 +1,55 @@ +# CronJob + +| Key | Type | Required | Helm Template | Default | Description | +| :-------------------------------------------------- | :------: | :------: | :-----------: | :----------------------: | :---------------------------------------------------- | +| workload.[workload-name].schedule | `string` | ✅ | ✅ | `""` | Define the schedule | +| workload.[workload-name].timezone | `string` | ❌ | ✅ | `{{ .Values.TZ }}` | Define the timezone | +| workload.[workload-name].concurrencyPolicy | `string` | ❌ | ✅ | `Forbid` | Define the concurrencyPolicy (Allow, Replace, Forbid) | +| workload.[workload-name].failedJobsHistoryLimit | `int` | ❌ | ❌ | `1` | Define the failedJobsHistoryLimit | +| workload.[workload-name].successfulJobsHistoryLimit | `int` | ❌ | ❌ | `3` | Define the successfulJobsHistoryLimit | +| workload.[workload-name].startingDeadlineSeconds | `int` | ❌ | ❌ | | Define the startingDeadlineSeconds | +| workload.[workload-name].completionMode | `string` | ❌ | ❌ | `NonIndexed` | Define the completionMode (Indexed, NonIndexed) | +| workload.[workload-name].backoffLimit | `int` | ❌ | ❌ | `5` | Define the backoffLimit | +| workload.[workload-name].completions | `int` | ❌ | ❌ | | Define the completions | +| workload.[workload-name].parallelism | `int` | ❌ | ❌ | `1` | Define the parallelism | +| workload.[workload-name].ttlSecondsAfterFinished | `int` | ❌ | ❌ | `120` | Define the ttlSecondsAfterFinished | +| workload.[workload-name].activeDeadlineSeconds | `int` | ❌ | ❌ | | Define the activeDeadlineSeconds | + +--- + +Notes: + +View common `keys` of `workload` in [workload Documentation](workload.md). + +--- + +Examples: + +```yaml +workload: + workload-name: + enabled: true + primary: true + type: CronJob + schedule: "{{ .Values.cron }}" + timezone: "{{ .Values.someTimezone }}" + concurrencyPolicy: Allow + failedJobsHistoryLimit: 2 + successfulJobsHistoryLimit: 4 + startingDeadlineSeconds: 100 + backoffLimit: 5 + completionMode: Indexed + completions: 5 + parallelism: 5 + ttlSecondsAfterFinished: 100 + activeDeadlineSeconds: 100 + podSpec: + restartPolicy: OnFailure + + other-workload-name: + enabled: true + primary: false + type: CronJob + schedule: "* * * * *" + podSpec: {} +``` diff --git a/library/common/1.0.0/docs/workload/deployment.md b/library/common/1.0.0/docs/workload/deployment.md new file mode 100644 index 0000000000..0980ff7c48 --- /dev/null +++ b/library/common/1.0.0/docs/workload/deployment.md @@ -0,0 +1,49 @@ +# Deployment + +| Key | Type | Required | Helm Template | Default | Description | +| :---------------------------------------------------- | :------: | :------: | :-----------: | :---------------------------------------------: | :------------------------------------------------------------------- | +| workload.[workload-name].strategy | `string` | ❌ | ❌ | `Recreate` | Define the strategy of the workload (Recreate, RollingUpdate) | +| workload.[workload-name].rollingUpdate | `dict` | ❌ | ❌ | `{}` | Holds the rollingUpdate options, Only when strategy is RollingUpdate | +| workload.[workload-name].rollingUpdate.maxUnavailable | `int` | ❌ | ❌ | `{{ .Values.fallbackDefaults.maxUnavailable }}` | Define the maxUnavailable, Only when strategy is RollingUpdate | +| workload.[workload-name].rollingUpdate.maxSurge | `int` | ❌ | ❌ | `{{ .Values.fallbackDefaults.maxSurge }}` | Define the maxSurge, Only when strategy is RollingUpdate | + +--- + +Notes: + +View common `keys` of `workload` in [workload Documentation](README.md). + +> Value of `workload.[workload-name].podSpec.restartPolicy` can only be `Always` for this type of workload + +--- + +Examples: + +```yaml +workload: + workload-name: + enabled: true + primary: true + type: Deployment + replicas: 1 + revisionHistoryLimit: 3 + strategy: Recreate + rollingUpdate: + maxUnavailable: 1 + maxSurge: 1 + podSpec: {} + + other-workload-name: + enabled: true + primary: false + type: Deployment + labels: {} + annotations: {} + replicas: 1 + revisionHistoryLimit: 3 + strategy: Recreate + rollingUpdate: + maxUnavailable: 1 + maxSurge: 1 + podSpec: {} +``` diff --git a/library/common/1.0.0/docs/workload/job.md b/library/common/1.0.0/docs/workload/job.md new file mode 100644 index 0000000000..69a4cad5d9 --- /dev/null +++ b/library/common/1.0.0/docs/workload/job.md @@ -0,0 +1,42 @@ +# Job + +| Key | Type | Required | Helm Template | Default | Description | +| :----------------------------------------------- | :------: | :------: | :-----------: | :----------: | :---------------------------------------------- | +| workload.[workload-name].completionMode | `string` | ❌ | ❌ | `NonIndexed` | Define the completionMode (Indexed, NonIndexed) | +| workload.[workload-name].backoffLimit | `int` | ❌ | ❌ | `5` | Define the backoffLimit | +| workload.[workload-name].completions | `int` | ❌ | ❌ | | Define the completions | +| workload.[workload-name].parallelism | `int` | ❌ | ❌ | `1` | Define the parallelism | +| workload.[workload-name].ttlSecondsAfterFinished | `int` | ❌ | ❌ | `120` | Define the ttlSecondsAfterFinished | +| workload.[workload-name].activeDeadlineSeconds | `int` | ❌ | ❌ | | Define the activeDeadlineSeconds | + +--- + +Notes: + +View common `keys` of `workload` in [workload Documentation](workload.md). + +--- + +Examples: + +```yaml +workload: + workload-name: + enabled: true + primary: true + type: Job + backoffLimit: 5 + completionMode: Indexed + completions: 5 + parallelism: 5 + ttlSecondsAfterFinished: 100 + activeDeadlineSeconds: 100 + podSpec: + restartPolicy: Never + + other-workload-name: + enabled: true + primary: false + type: Job + podSpec: {} +``` diff --git a/library/common/1.0.0/templates/class/_configmap.tpl b/library/common/1.0.0/templates/class/_configmap.tpl new file mode 100644 index 0000000000..993fb7530e --- /dev/null +++ b/library/common/1.0.0/templates/class/_configmap.tpl @@ -0,0 +1,35 @@ +{{/* Configmap Class */}} +{{/* Call this template: +{{ include "ix.v1.common.class.configmap" (dict "rootCtx" $ "objectData" $objectData) }} + +rootCtx: The root context of the chart. +objectData: + name: The name of the configmap. + labels: The labels of the configmap. + annotations: The annotations of the configmap. + data: The data of the configmap. +*/}} + +{{- define "ix.v1.common.class.configmap" -}} + + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ $objectData.name }} + {{- $labels := (mustMerge ($objectData.labels | default dict) (include "ix.v1.common.lib.metadata.allLabels" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "labels" $labels) | trim) }} + labels: + {{- . | nindent 4 }} + {{- end -}} + {{- $annotations := (mustMerge ($objectData.annotations | default dict) (include "ix.v1.common.lib.metadata.allAnnotations" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "annotations" $annotations) | trim) }} + annotations: + {{- . | nindent 4 }} + {{- end }} +data: + {{- tpl (toYaml $objectData.data) $rootCtx | nindent 2 }} + {{/* This comment is here to add a new line */}} +{{- end -}} diff --git a/library/common/1.0.0/templates/class/_cronjob.tpl b/library/common/1.0.0/templates/class/_cronjob.tpl new file mode 100644 index 0000000000..96e718ef33 --- /dev/null +++ b/library/common/1.0.0/templates/class/_cronjob.tpl @@ -0,0 +1,51 @@ +{{/* CronJob Class */}} +{{/* Call this template: +{{ include "ix.v1.common.class.cronjob" (dict "rootCtx" $ "objectData" $objectData) }} + +rootCtx: The root context of the chart. +objectData: The object data to be used to render the CronJob. +*/}} + +{{- define "ix.v1.common.class.cronjob" -}} + + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + {{- include "ix.v1.common.lib.workload.cronjobValidation" (dict "objectData" $objectData) }} +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: {{ $objectData.name }} + {{- $labels := (mustMerge ($objectData.labels | default dict) (include "ix.v1.common.lib.metadata.allLabels" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "labels" $labels) | trim) }} + labels: + {{- . | nindent 4 }} + {{- end -}} + {{- $annotations := (mustMerge ($objectData.annotations | default dict) (include "ix.v1.common.lib.metadata.allAnnotations" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "annotations" $annotations) | trim) }} + annotations: + {{- . | nindent 4 }} + {{- end }} +spec: + {{- include "ix.v1.common.lib.workload.cronjobSpec" (dict "rootCtx" $rootCtx "objectData" $objectData) | nindent 2 }} + template: + metadata: + {{- $labels := (mustMerge ($objectData.podSpec.labels | default dict) + (include "ix.v1.common.lib.metadata.allLabels" $rootCtx | fromYaml) + (include "ix.v1.common.lib.metadata.podLabels" $rootCtx | fromYaml) + (include "ix.v1.common.lib.metadata.selectorLabels" (dict "rootCtx" $rootCtx "objectType" "pod" "objectName" $objectData.shortName) | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "labels" $labels) | trim) }} + labels: + {{- . | nindent 12 }} + {{- end -}} + {{- $annotations := (mustMerge ($objectData.podSpec.annotations | default dict) + (include "ix.v1.common.lib.metadata.allAnnotations" $rootCtx | fromYaml) + (include "ix.v1.common.lib.metadata.externalInterfacePodAnnotations" (dict "rootCtx" $rootCtx "objectData" $objectData) | fromYaml) + (include "ix.v1.common.lib.metadata.podAnnotations" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "annotations" $annotations) | trim) }} + annotations: + {{- . | nindent 12 }} + {{- end }} + spec: + {{- include "ix.v1.common.lib.workload.pod" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 10 }} +{{- end -}} diff --git a/library/common/1.0.0/templates/class/_deployment.tpl b/library/common/1.0.0/templates/class/_deployment.tpl new file mode 100644 index 0000000000..c707acaef9 --- /dev/null +++ b/library/common/1.0.0/templates/class/_deployment.tpl @@ -0,0 +1,54 @@ +{{/* Deployment Class */}} +{{/* Call this template: +{{ include "ix.v1.common.class.deployment" (dict "rootCtx" $ "objectData" $objectData) }} + +rootCtx: The root context of the chart. +objectData: The object data to be used to render the Deployment. +*/}} + +{{- define "ix.v1.common.class.deployment" -}} + + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + {{- include "ix.v1.common.lib.workload.deploymentValidation" (dict "objectData" $objectData) }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ $objectData.name }} + {{- $labels := (mustMerge ($objectData.labels | default dict) (include "ix.v1.common.lib.metadata.allLabels" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "labels" $labels) | trim) }} + labels: + {{- . | nindent 4 }} + {{- end -}} + {{- $annotations := (mustMerge ($objectData.annotations | default dict) (include "ix.v1.common.lib.metadata.allAnnotations" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "annotations" $annotations) | trim) }} + annotations: + {{- . | nindent 4 }} + {{- end }} +spec: + {{- include "ix.v1.common.lib.workload.deploymentSpec" (dict "rootCtx" $rootCtx "objectData" $objectData) | nindent 2 }} + selector: + matchLabels: + {{- include "ix.v1.common.lib.metadata.selectorLabels" (dict "rootCtx" $rootCtx "objectType" "pod" "objectName" $objectData.shortName) | trim | nindent 6 }} + template: + metadata: + {{- $labels := (mustMerge ($objectData.podSpec.labels | default dict) + (include "ix.v1.common.lib.metadata.allLabels" $rootCtx | fromYaml) + (include "ix.v1.common.lib.metadata.podLabels" $rootCtx | fromYaml) + (include "ix.v1.common.lib.metadata.selectorLabels" (dict "rootCtx" $rootCtx "objectType" "pod" "objectName" $objectData.shortName) | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "labels" $labels) | trim) }} + labels: + {{- . | nindent 8 }} + {{- end -}} + {{- $annotations := (mustMerge ($objectData.podSpec.annotations | default dict) + (include "ix.v1.common.lib.metadata.allAnnotations" $rootCtx | fromYaml) + (include "ix.v1.common.lib.metadata.externalInterfacePodAnnotations" (dict "rootCtx" $rootCtx "objectData" $objectData) | fromYaml) + (include "ix.v1.common.lib.metadata.podAnnotations" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "annotations" $annotations) | trim) }} + annotations: + {{- . | nindent 8 }} + {{- end }} + spec: + {{- include "ix.v1.common.lib.workload.pod" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 6 }} +{{- end -}} diff --git a/library/common/1.0.0/templates/class/_job.tpl b/library/common/1.0.0/templates/class/_job.tpl new file mode 100644 index 0000000000..34c54d8639 --- /dev/null +++ b/library/common/1.0.0/templates/class/_job.tpl @@ -0,0 +1,51 @@ +{{/* Job Class */}} +{{/* Call this template: +{{ include "ix.v1.common.class.job" (dict "rootCtx" $ "objectData" $objectData) }} + +rootCtx: The root context of the chart. +objectData: The object data to be used to render the Job. +*/}} + +{{- define "ix.v1.common.class.job" -}} + + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + {{- include "ix.v1.common.lib.workload.jobValidation" (dict "objectData" $objectData) }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ $objectData.name }} + {{- $labels := (mustMerge ($objectData.labels | default dict) (include "ix.v1.common.lib.metadata.allLabels" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "labels" $labels) | trim) }} + labels: + {{- . | nindent 4 }} + {{- end -}} + {{- $annotations := (mustMerge ($objectData.annotations | default dict) (include "ix.v1.common.lib.metadata.allAnnotations" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "annotations" $annotations) | trim) }} + annotations: + {{- . | nindent 4 }} + {{- end }} +spec: + {{- include "ix.v1.common.lib.workload.jobSpec" (dict "rootCtx" $rootCtx "objectData" $objectData) | nindent 2 }} + template: + metadata: + {{- $labels := (mustMerge ($objectData.podSpec.labels | default dict) + (include "ix.v1.common.lib.metadata.allLabels" $rootCtx | fromYaml) + (include "ix.v1.common.lib.metadata.podLabels" $rootCtx | fromYaml) + (include "ix.v1.common.lib.metadata.selectorLabels" (dict "rootCtx" $rootCtx "objectType" "pod" "objectName" $objectData.shortName) | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "labels" $labels) | trim) }} + labels: + {{- . | nindent 8 }} + {{- end -}} + {{- $annotations := (mustMerge ($objectData.podSpec.annotations | default dict) + (include "ix.v1.common.lib.metadata.allAnnotations" $rootCtx | fromYaml) + (include "ix.v1.common.lib.metadata.externalInterfacePodAnnotations" (dict "rootCtx" $rootCtx "objectData" $objectData) | fromYaml) + (include "ix.v1.common.lib.metadata.podAnnotations" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "annotations" $annotations) | trim) }} + annotations: + {{- . | nindent 8 }} + {{- end }} + spec: + {{- include "ix.v1.common.lib.workload.pod" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 6 }} +{{- end -}} diff --git a/library/common/1.0.0/templates/class/_networkAttachmentDefinition.tpl b/library/common/1.0.0/templates/class/_networkAttachmentDefinition.tpl new file mode 100644 index 0000000000..bd6e1993bf --- /dev/null +++ b/library/common/1.0.0/templates/class/_networkAttachmentDefinition.tpl @@ -0,0 +1,34 @@ +{{/* Network Attachment Definition Class */}} +{{/* Call this template: +{{ include "ix.v1.common.class.networkAttachmentDefinition" (dict "rootCtx" $ "objectData" $objectData) }} + +rootCtx: The root context of the chart. +objectData: + name: The name of the Network Attachment Definition. + labels: The labels of the Network Attachment Definition. + annotations: The annotations of the Network Attachment Definition. + config: The config of the interface +*/}} + +{{- define "ix.v1.common.class.networkAttachmentDefinition" -}} + + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData }} +--- +apiVersion: k8s.cni.cncf.io/v1 +kind: NetworkAttachmentDefinition +metadata: + name: {{ $objectData.name }} + {{- $labels := (include "ix.v1.common.lib.metadata.allLabels" $rootCtx | fromYaml) | default dict -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "labels" $labels) | trim) }} + labels: + {{- . | nindent 4 }} + {{- end -}} + {{- $annotations := (include "ix.v1.common.lib.metadata.allAnnotations" $rootCtx | fromYaml) | default dict -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "annotations" $annotations) | trim) }} + annotations: + {{- . | nindent 4 }} + {{- end }} +spec: + config: {{ $objectData.config | squote }} +{{- end -}} diff --git a/library/common/1.0.0/templates/class/_rbac.tpl b/library/common/1.0.0/templates/class/_rbac.tpl new file mode 100644 index 0000000000..00dd281fcf --- /dev/null +++ b/library/common/1.0.0/templates/class/_rbac.tpl @@ -0,0 +1,64 @@ +{{/* RBAC Class */}} +{{/* Call this template: +{{ include "ix.v1.common.class.rbac" (dict "rootCtx" $ "objectData" $objectData) }} + +rootCtx: The root context of the chart. +objectData: + name: The name of the rbac. + labels: The labels of the rbac. + annotations: The annotations of the rbac. + clusterWide: Whether the rbac is cluster wide or not. + rules: The rules of the rbac. + subjects: The subjects of the rbac. +*/}} + +{{- define "ix.v1.common.class.rbac" -}} + + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: {{ ternary "ClusterRole" "Role" $objectData.clusterWide }} +metadata: + name: {{ $objectData.name }} + {{- if not $objectData.clusterWide }} + namespace: {{ $rootCtx.Release.Namespace }} + {{- end }} + {{- $labels := (mustMerge ($objectData.labels | default dict) (include "ix.v1.common.lib.metadata.allLabels" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "labels" $labels) | trim) }} + labels: + {{- . | nindent 4 }} + {{- end -}} + {{- $annotations := (mustMerge ($objectData.annotations | default dict) (include "ix.v1.common.lib.metadata.allAnnotations" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "annotations" $annotations) | trim) }} + annotations: + {{- . | nindent 4 }} + {{- end }} +rules: + {{- include "ix.v1.common.lib.rbac.rules" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 2 }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: {{ ternary "ClusterRoleBinding" "RoleBinding" $objectData.clusterWide }} +metadata: + name: {{ $objectData.name }} + {{- if not $objectData.clusterWide }} + namespace: {{ $rootCtx.Release.Namespace }} + {{- end }} + {{- $labels := (mustMerge ($objectData.labels | default dict) (include "ix.v1.common.lib.metadata.allLabels" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "labels" $labels) | trim) }} + labels: + {{- . | nindent 4 }} + {{- end -}} + {{- $annotations := (mustMerge ($objectData.annotations | default dict) (include "ix.v1.common.lib.metadata.allAnnotations" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "annotations" $annotations) | trim) }} + annotations: + {{- . | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: {{ ternary "ClusterRole" "Role" $objectData.clusterWide }} + name: {{ $objectData.name }} +subjects: + {{- include "ix.v1.common.lib.rbac.serviceAccount" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 2 }} + {{- include "ix.v1.common.lib.rbac.subjects" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 2 }} +{{- end -}} diff --git a/library/common/1.0.0/templates/class/_secret.tpl b/library/common/1.0.0/templates/class/_secret.tpl new file mode 100644 index 0000000000..7f0ec9f7e6 --- /dev/null +++ b/library/common/1.0.0/templates/class/_secret.tpl @@ -0,0 +1,56 @@ +{{/* Secret Class */}} +{{/* Call this template: +{{ include "ix.v1.common.class.secret" (dict "rootCtx" $ "objectData" $objectData) }} + +rootCtx: The root context of the chart. +objectData: + name: The name of the secret. + labels: The labels of the secret. + annotations: The annotations of the secret. + type: The type of the secret. + data: The data of the secret. +*/}} + +{{- define "ix.v1.common.class.secret" -}} + + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + {{- $secretType := "Opaque" -}} + + {{- if eq $objectData.type "certificate" -}} + {{- $secretType = "kubernetes.io/tls" -}} + {{- else if eq $objectData.type "imagePullSecret" -}} + {{- $secretType = "kubernetes.io/dockerconfigjson" -}} + {{- else if $objectData.type -}} + {{- $secretType = $objectData.type -}} + {{- end }} +--- +apiVersion: v1 +kind: Secret +type: {{ $secretType }} +metadata: + name: {{ $objectData.name }} + {{- $labels := (mustMerge ($objectData.labels | default dict) (include "ix.v1.common.lib.metadata.allLabels" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "labels" $labels) | trim) }} + labels: + {{- . | nindent 4 }} + {{- end -}} + {{- $annotations := (mustMerge ($objectData.annotations | default dict) (include "ix.v1.common.lib.metadata.allAnnotations" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "annotations" $annotations) | trim) }} + annotations: + {{- . | nindent 4 }} + {{- end -}} + {{- if (mustHas $objectData.type (list "certificate" "imagePullSecret")) }} +data: + {{- if eq $objectData.type "certificate" }} + tls.crt: {{ $objectData.data.certificate | trim | b64enc }} + tls.key: {{ $objectData.data.privatekey | trim | b64enc }} + {{- else if eq $objectData.type "imagePullSecret" }} + .dockerconfigjson: {{ $objectData.data | trim | b64enc }} + {{- end -}} + {{- else }} +stringData: + {{- tpl (toYaml $objectData.data) $rootCtx | nindent 2 }} + {{/* This comment is here to add a new line */}} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/class/_service.tpl b/library/common/1.0.0/templates/class/_service.tpl new file mode 100644 index 0000000000..dba5f6db51 --- /dev/null +++ b/library/common/1.0.0/templates/class/_service.tpl @@ -0,0 +1,75 @@ +{{/* Service Class */}} +{{/* Call this template: +{{ include "ix.v1.common.class.service" (dict "rootCtx" $ "objectData" $objectData) }} + +rootCtx: The root context of the chart. +objectData: The service data, that will be used to render the Service object. +*/}} + +{{- define "ix.v1.common.class.service" -}} + + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $svcType := $objectData.type | default $rootCtx.Values.fallbackDefaults.serviceType -}} + + {{/* Init variables */}} + {{- $hasHostPort := false -}} + {{- $hostNetwork := false -}} + {{- $podValues := dict -}} + + {{/* Get Pod Values based on the selector (or the absence of it) */}} + {{- $podValues = fromJson (include "ix.v1.common.lib.helpers.getSelectedPodValues" (dict "rootCtx" $rootCtx "objectData" $objectData "caller" "Service")) -}} + + {{- if $podValues -}} + {{/* Get Pod hostNetwork configuration */}} + {{- $hostNetwork = include "ix.v1.common.lib.pod.hostNetwork" (dict "rootCtx" $rootCtx "objectData" $podValues) -}} + {{- end -}} + + {{- range $portName, $port := $objectData.ports -}} + {{- if $port.enabled -}} + {{- if and (hasKey $port "hostPort") $port.hostPort -}} + {{- $hasHostPort = true -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{/* When hostNetwork is set on the pod, force ClusterIP, so services wont try to bind the same ports on the host */}} + {{- if or (and (kindIs "bool" $hostNetwork) $hostNetwork) (and (kindIs "string" $hostNetwork) (eq $hostNetwork "true")) -}} + {{- $svcType = "ClusterIP" -}} + {{- end -}} + + {{/* When hostPort is defined, force ClusterIP aswell */}} + {{- if $hasHostPort -}} + {{- $svcType = "ClusterIP" -}} + {{- end }} + +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ $objectData.name }} + {{- $labels := (mustMerge ($objectData.labels | default dict) (include "ix.v1.common.lib.metadata.allLabels" $rootCtx | fromYaml) + (include "ix.v1.common.lib.metadata.selectorLabels" (dict "rootCtx" $rootCtx "objectType" "service" "objectName" $objectData.name) | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "labels" $labels) | trim) }} + labels: + {{- . | nindent 4 }} + {{- end -}} + {{- $annotations := (mustMerge ($objectData.annotations | default dict) (include "ix.v1.common.lib.metadata.allAnnotations" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "annotations" $annotations) | trim) }} + annotations: + {{- . | nindent 4 }} + {{- end }} +spec: + {{- if eq $svcType "ClusterIP" -}} + {{- include "ix.v1.common.lib.service.spec.clusterIP" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 2 -}} + {{- else if eq $svcType "NodePort" -}} + {{- include "ix.v1.common.lib.service.spec.nodePort" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 2 -}} + {{- end -}} + {{- with (include "ix.v1.common.lib.service.ports" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim) }} + ports: + {{- . | nindent 4 }} + {{- end }} + selector: + {{- include "ix.v1.common.lib.metadata.selectorLabels" (dict "rootCtx" $rootCtx "objectType" "pod" "objectName" $podValues.shortName) | trim | nindent 4 -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/class/_serviceAccount.tpl b/library/common/1.0.0/templates/class/_serviceAccount.tpl new file mode 100644 index 0000000000..70eac18b72 --- /dev/null +++ b/library/common/1.0.0/templates/class/_serviceAccount.tpl @@ -0,0 +1,33 @@ +{{/* Service Account Class */}} +{{/* Call this template: +{{ include "ix.v1.common.class.serviceAccount" (dict "rootCtx" $ "objectData" $objectData) }} + +rootCtx: The root context of the chart. +objectData: + name: The name of the serviceAccount. + labels: The labels of the serviceAccount. + annotations: The annotations of the serviceAccount. +*/}} + +{{- define "ix.v1.common.class.serviceAccount" -}} + + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ $objectData.name }} + {{- $labels := (mustMerge ($objectData.labels | default dict) (include "ix.v1.common.lib.metadata.allLabels" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "labels" $labels) | trim) }} + labels: + {{- . | nindent 4 }} + {{- end -}} + {{- $annotations := (mustMerge ($objectData.annotations | default dict) (include "ix.v1.common.lib.metadata.allAnnotations" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "annotations" $annotations) | trim) }} + annotations: + {{- . | nindent 4 }} + {{- end -}} + {{- /* We already allow to override it on per pod basis, let's hard default to false globally */}} +automountServiceAccountToken: false +{{- end -}} diff --git a/library/common/1.0.0/templates/helpers/_envDupeCheck.tpl b/library/common/1.0.0/templates/helpers/_envDupeCheck.tpl new file mode 100644 index 0000000000..1992e434b4 --- /dev/null +++ b/library/common/1.0.0/templates/helpers/_envDupeCheck.tpl @@ -0,0 +1,23 @@ +{{/* Check Env for Duplicates */}} +{{/* Call this template: +{{ include "ix.v1.common.helper.container.envDupeCheck" (dict "rootCtx" $ "objectData" $objectData "source" $source "key" $key) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.helper.container.envDupeCheck" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $source := .source -}} + {{- $type := .type -}} + {{- $key := .key -}} + + {{- $dupeEnv := (get $objectData.envDupe $key) -}} + + {{- if $dupeEnv -}} + {{- fail (printf "Container - Environment Variable [%s] in [%s] tried to override the Environment Variable that is already defined in [%s]" $key $source $dupeEnv.source) -}} + {{- end -}} + + {{- $_ := set $objectData.envDupe $key (dict "source" $source) -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/helpers/_getPortRange.tpl b/library/common/1.0.0/templates/helpers/_getPortRange.tpl new file mode 100644 index 0000000000..e8e0fd2d8e --- /dev/null +++ b/library/common/1.0.0/templates/helpers/_getPortRange.tpl @@ -0,0 +1,60 @@ +{{/* Returns Lowest and Highest ports assigned to the any container in the pod */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.helpers.securityContext.getPortRange" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the Pod. +*/}} +{{- define "ix.v1.common.lib.helpers.securityContext.getPortRange" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{ $portRange := (dict "high" 0 "low" 0) }} + + {{- range $name, $service := $rootCtx.Values.service -}} + {{- $selected := false -}} + {{/* If service is enabled... */}} + {{- if $service.enabled -}} + + {{/* If there is a selector */}} + {{- if $service.targetSelector -}} + + {{/* And pod is selected */}} + {{- if eq $service.targetSelector $objectData.shortName -}} + {{- $selected = true -}} + {{- end -}} + + {{- else -}} + {{/* If no selector is defined but pod is primary */}} + {{- if $objectData.primary -}} + {{- $selected = true -}} + {{- end -}} + + {{- end -}} + {{- end -}} + + {{- if $selected -}} + {{- range $name, $portValues := $service.ports -}} + {{- if $portValues.enabled -}} + + {{- $portToCheck := ($portValues.targetPort | default $portValues.port) -}} + {{- if kindIs "string" $portToCheck -}} + {{/* Helm stores ints as floats, so convert string to float before comparing */}} + {{- $portToCheck = (tpl $portToCheck $rootCtx) | float64 -}} + {{- end -}} + + {{- if or (not $portRange.low) (lt $portToCheck ($portRange.low | float64)) -}} + {{- $_ := set $portRange "low" $portToCheck -}} + {{- end -}} + + {{- if or (not $portRange.high) (gt $portToCheck ($portRange.high | float64)) -}} + {{- $_ := set $portRange "high" $portToCheck -}} + {{- end -}} + + {{- end -}} + {{- end -}} + {{- end -}} + + {{- end -}} + + {{- $portRange | toJson -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/helpers/_getSelectedPod.tpl b/library/common/1.0.0/templates/helpers/_getSelectedPod.tpl new file mode 100644 index 0000000000..886b134610 --- /dev/null +++ b/library/common/1.0.0/templates/helpers/_getSelectedPod.tpl @@ -0,0 +1,47 @@ +{{/* Service - Get Selected Pod */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.helpers.getSelectedPodValues" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} +objectData: The object data of the service +rootCtx: The root context of the chart. +*/}} + +{{- define "ix.v1.common.lib.helpers.getSelectedPodValues" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + {{- $caller := .caller -}} + + {{- $podValues := dict -}} + {{- with $objectData.targetSelector -}} + {{- $podValues = mustDeepCopy (get $rootCtx.Values.workload .) -}} + + {{- if not $podValues -}} + {{- fail (printf "%s - Selected pod [%s] is not defined" $caller .) -}} + {{- end -}} + + {{- if not $podValues.enabled -}} + {{- fail (printf "%s - Selected pod [%s] is not enabled" $caller .) -}} + {{- end -}} + + {{/* While we know the shortName from targetSelector, let's set it explicitly + So service can reference this directly, to match the behaviour of a service + without targetSelector defined (assumes "use primary") */}} + {{- $_ := set $podValues "shortName" . -}} + {{- else -}} + + {{/* If no targetSelector is defined, we assume the service is using the primary pod */}} + {{/* Also no need to check for multiple primaries here, it's already done on the workload validation */}} + {{- range $podName, $pod := $rootCtx.Values.workload -}} + {{- if $pod.enabled -}} + {{- if $pod.primary -}} + {{- $podValues = mustDeepCopy $pod -}} + {{/* Set the shortName so service can use this on selector */}} + {{- $_ := set $podValues "shortName" $podName -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- end -}} + + {{/* Return values in Json, to preserve types */}} + {{ $podValues | toJson }} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/certificate/_getData.tpl b/library/common/1.0.0/templates/lib/certificate/_getData.tpl new file mode 100644 index 0000000000..da3754e47e --- /dev/null +++ b/library/common/1.0.0/templates/lib/certificate/_getData.tpl @@ -0,0 +1,40 @@ +{{/* Get Certificate Data */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.certificate.getData" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} +rootCtx: The root context of the chart. +objectData: The object data of the certificate +*/}} +{{- define "ix.v1.common.lib.certificate.getData" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} + + {{- $certID := (toString $objectData.id) -}} + + {{/* Make sure certificate exists */}} + {{- if hasKey $rootCtx.Values "ixCertificates" -}} + {{- if not $rootCtx.Values.ixCertificates -}} + {{- fail "Certificate - Expected non-empty " -}} + {{- end -}} + + {{- if not (hasKey $rootCtx.Values.ixCertificates $certID) -}} + {{- fail (printf "Certificate - Expected certificate with [%q] to exist in " $certID) -}} + {{- end -}} + {{- end -}} + + {{- $data := get $rootCtx.Values.ixCertificates $certID -}} + + {{- range $flag := (list "revoked" "expired") -}} + {{- if (get $data $flag) -}} + {{- fail (printf "Certificate - Expected non-%s certificate with [%q]" $flag $certID) -}} + {{- end -}} + {{- end -}} + + {{- range $key := (list "certificate" "privatekey") -}} + {{- if not (get $data $key) -}} + {{- fail (printf "Certificate - Expected non-empty [%s] in certificate with [%q] in " $key $certID) -}} + {{- end -}} + {{- end -}} + + + {{- $data | toJson -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/certificate/_validation.tpl b/library/common/1.0.0/templates/lib/certificate/_validation.tpl new file mode 100644 index 0000000000..f9a3e7f6f3 --- /dev/null +++ b/library/common/1.0.0/templates/lib/certificate/_validation.tpl @@ -0,0 +1,18 @@ +{{/* Certificate Validation */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.certificate.validation" (dict "objectData" $objectData) -}} +objectData: The object data of the certificate. +*/}} + +{{- define "ix.v1.common.lib.certificate.validation" -}} + {{- $objectData := .objectData -}} + + {{- if not $objectData.id -}} + {{- fail "Certificate - Expected non-empty " -}} + {{- end -}} + + {{- if and $objectData.targetSelector (not (kindIs "map" $objectData.targetSelector)) -}} + {{- fail (printf "Certificate - Expected to be a [map], but got [%s]" (kindOf $objectData.targetSelector)) -}} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/chart/_names.tpl b/library/common/1.0.0/templates/lib/chart/_names.tpl new file mode 100644 index 0000000000..f8f6c944a8 --- /dev/null +++ b/library/common/1.0.0/templates/lib/chart/_names.tpl @@ -0,0 +1,41 @@ +{{/* Contains functions for generating names */}} + +{{/* Returns the name of the Chart */}} +{{- define "ix.v1.common.lib.chart.names.name" -}} + + {{- .Chart.Name | lower | trunc 63 | trimSuffix "-" -}} + +{{- end -}} + +{{/* Returns the fullname of the Chart */}} +{{- define "ix.v1.common.lib.chart.names.fullname" -}} + + {{- $name := include "ix.v1.common.lib.chart.names.name" . -}} + + {{- if contains $name .Release.Name -}} + {{- $name = .Release.Name -}} + {{- else -}} + {{- $name = printf "%s-%s" .Release.Name $name -}} + {{- end -}} + + {{- $name | lower | trunc 63 | trimSuffix "-" -}} + +{{- end -}} + +{{/* Validates names */}} +{{- define "ix.v1.common.lib.chart.names.validation" -}} + + {{- $name := .name -}} + + {{- if not (mustRegexMatch "^[a-z0-9]([a-z0-9-]){1,61}[a-z0-9]$" $name) -}} + {{- fail (printf "Name [%s] is not valid. Must start and end with an alphanumeric lowercase character. It can contain '-'. And must be at most 63 characters." $name) -}} + {{- end -}} + +{{- end -}} + +{{/* Create chart name and version as used by the chart label */}} +{{- define "ix.v1.common.lib.chart.names.chart" -}} + + {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/chart/_notes.tpl b/library/common/1.0.0/templates/lib/chart/_notes.tpl new file mode 100644 index 0000000000..6745605966 --- /dev/null +++ b/library/common/1.0.0/templates/lib/chart/_notes.tpl @@ -0,0 +1,21 @@ +{{- define "ix.v1.common.lib.chart.notes" -}} + + {{- include "ix.v1.common.lib.chart.header" . -}} + + {{- include "ix.v1.common.lib.chart.custom" . -}} + + {{- include "ix.v1.common.lib.chart.footer" . -}} + +{{- end -}} + +{{- define "ix.v1.common.lib.chart.header" -}} + {{- tpl $.Values.notes.header $ | nindent 0 }} +{{- end -}} + +{{- define "ix.v1.common.lib.chart.custom" -}} + {{- tpl $.Values.notes.custom $ | nindent 0 }} +{{- end -}} + +{{- define "ix.v1.common.lib.chart.footer" -}} + {{- tpl $.Values.notes.footer $ | nindent 0 }} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/configmap/_validation.tpl b/library/common/1.0.0/templates/lib/configmap/_validation.tpl new file mode 100644 index 0000000000..321d710f7e --- /dev/null +++ b/library/common/1.0.0/templates/lib/configmap/_validation.tpl @@ -0,0 +1,21 @@ +{{/* Configmap Validation */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.configmap.validation" (dict "objectData" $objectData) -}} +objectData: + labels: The labels of the configmap. + annotations: The annotations of the configmap. + data: The data of the configmap. +*/}} + +{{- define "ix.v1.common.lib.configmap.validation" -}} + {{- $objectData := .objectData -}} + + {{- if not $objectData.data -}} + {{- fail "ConfigMap - Expected non-empty " -}} + {{- end -}} + + {{- if not (kindIs "map" $objectData.data) -}} + {{- fail (printf "ConfigMap - Expected to be a dictionary, but got [%v]" (kindOf $objectData.data)) -}} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/container/_args.tpl b/library/common/1.0.0/templates/lib/container/_args.tpl new file mode 100644 index 0000000000..e0be06815c --- /dev/null +++ b/library/common/1.0.0/templates/lib/container/_args.tpl @@ -0,0 +1,22 @@ +{{/* Returns args list */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.args" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.lib.container.args" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- range $key := (list "args" "extraArgs") -}} + {{- with (get $objectData $key) -}} + {{- if kindIs "string" . }} +- {{ tpl . $rootCtx | quote }} + {{- else if kindIs "slice" . -}} + {{- range $arg := . }} +- {{ tpl $arg $rootCtx | quote }} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/container/_command.tpl b/library/common/1.0.0/templates/lib/container/_command.tpl new file mode 100644 index 0000000000..6349999688 --- /dev/null +++ b/library/common/1.0.0/templates/lib/container/_command.tpl @@ -0,0 +1,18 @@ +{{/* Returns command list */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.command" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.lib.container.command" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- if kindIs "string" $objectData.command }} +- {{ tpl $objectData.command $rootCtx | quote }} + {{- else if kindIs "slice" $objectData.command -}} + {{- range $objectData.command }} +- {{ tpl . $rootCtx | quote }} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/container/_env.tpl b/library/common/1.0.0/templates/lib/container/_env.tpl new file mode 100644 index 0000000000..e22555dbef --- /dev/null +++ b/library/common/1.0.0/templates/lib/container/_env.tpl @@ -0,0 +1,84 @@ +{{/* Returns Env */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.env" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.lib.container.env" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- range $k, $v := $objectData.env -}} + {{- include "ix.v1.common.helper.container.envDupeCheck" (dict "rootCtx" $rootCtx "objectData" $objectData "source" "env" "key" $k) }} +- name: {{ $k | quote }} + {{- if not (kindIs "map" $v) }} + value: {{ tpl (toString $v) $rootCtx | quote }} + {{- else if kindIs "map" $v }} + valueFrom: + {{- $refs := (list "configMapKeyRef" "secretKeyRef" "fieldRef") -}} + {{- if or (ne (len ($v | keys)) 1) (not (mustHas ($v | keys | first) $refs)) -}} + {{- fail (printf "Container - Expected with a ref to have one of [%s], but got [%s]" (join ", " $refs) (join ", " ($v | keys | sortAlpha))) -}} + {{- end -}} + + {{- $expandName := true -}} + {{- $name := "" -}} + + {{- range $key := (list "configMapKeyRef" "secretKeyRef") -}} + {{- if hasKey $v $key }} + {{ $key }}: + {{- $obj := get $v $key -}} + {{- if not $obj.name -}} + {{- fail (printf "Container - Expected non-empty " $key) -}} + {{- end -}} + + {{- if not $obj.key -}} + {{- fail (printf "Container - Expected non-empty " $key) -}} + {{- end }} + key: {{ $obj.key | quote }} + + {{- $name = tpl $obj.name $rootCtx -}} + {{- if kindIs "bool" $obj.expandObjectName -}} + {{- $expandName = $obj.expandObjectName -}} + {{- end -}} + + {{- if $expandName -}} + {{- $item := ($key | trimSuffix "KeyRef" | lower) -}} + + {{- $data := (get $rootCtx.Values $item) -}} + {{- $data = (get $data $name) -}} + + {{- if not $data -}} + {{- fail (printf "Container - Expected in the referenced %s [%s] to be defined" (camelcase $item) $name) -}} + {{- end -}} + + {{- $found := false -}} + {{- range $k, $v := $data.data -}} + {{- if eq $k $obj.key -}} + {{- $found = true -}} + {{- end -}} + {{- end -}} + + {{- if not $found -}} + {{- fail (printf "Container - Expected in the referenced key [%s] in %s [%s] to be defined" $obj.key (camelcase $item) $name) -}} + {{- end -}} + + {{- $name = (printf "%s-%s" (include "ix.v1.common.lib.chart.names.fullname" $rootCtx) $name) -}} + {{- end }} + name: {{ $name | quote }} + {{- end -}} + {{- end -}} + + {{- if hasKey $v "fieldRef" }} + fieldRef: + {{- if not $v.fieldRef.fieldPath -}} + {{- fail "Container - Expected non-empty " -}} + {{- end }} + fieldPath: {{ $v.fieldRef.fieldPath | quote }} + {{- if $v.fieldRef.apiVersion }} + apiVersion: {{ $v.fieldRef.apiVersion | quote }} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/container/_envFrom.tpl b/library/common/1.0.0/templates/lib/container/_envFrom.tpl new file mode 100644 index 0000000000..9cec8a1a34 --- /dev/null +++ b/library/common/1.0.0/templates/lib/container/_envFrom.tpl @@ -0,0 +1,59 @@ +{{/* Returns Env From */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.envFrom" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.lib.container.envFrom" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $refs := (list "configMapRef" "secretRef") -}} + {{- range $envFrom := $objectData.envFrom -}} + {{- if and (not $envFrom.secretRef) (not $envFrom.configMapRef) -}} + {{- fail (printf "Container - Expected entry to have one of [%s]" (join ", " $refs)) -}} + {{- end -}} + + {{- if and $envFrom.secretRef $envFrom.configMapRef -}} + {{- fail (printf "Container - Expected entry to have only one of [%s], but got both" (join ", " $refs)) -}} + {{- end -}} + + {{- range $ref := $refs -}} + {{- with (get $envFrom $ref) -}} + {{- if not .name -}} + {{- fail (printf "Container - Expected non-empty " $ref) -}} + {{- end -}} + + {{- $objectName := tpl .name $rootCtx -}} + + {{- $expandName := true -}} + {{- if kindIs "bool" .expandObjectName -}} + {{- $expandName = .expandObjectName -}} + {{- end -}} + + {{- if $expandName -}} + {{- $object := dict -}} + {{- $source := "" -}} + {{- if eq $ref "configMapRef" -}} + {{- $object = (get $rootCtx.Values.configmap $objectName) -}} + {{- $source = "ConfigMap" -}} + {{- else if eq $ref "secretRef" -}} + {{- $object = (get $rootCtx.Values.secret $objectName) -}} + {{- $source = "Secret" -}} + {{- end -}} + + {{- if not $object -}} + {{- fail (printf "Container - Expected %s [%s] defined in to exist" $source $objectName) -}} + {{- end -}} + {{- range $k, $v := $object.data -}} + {{- include "ix.v1.common.helper.container.envDupeCheck" (dict "rootCtx" $rootCtx "objectData" $objectData "source" (printf "%s - %s" $source $objectName) "key" $k) -}} + {{- end -}} + + {{- $objectName = (printf "%s-%s" (include "ix.v1.common.lib.chart.names.fullname" $rootCtx) $objectName) -}} + {{- end }} +- {{ $ref }}: + name: {{ $objectName | quote }} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/container/_envList.tpl b/library/common/1.0.0/templates/lib/container/_envList.tpl new file mode 100644 index 0000000000..d7a9c9e5c2 --- /dev/null +++ b/library/common/1.0.0/templates/lib/container/_envList.tpl @@ -0,0 +1,19 @@ +{{/* Returns Env List */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.envList" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.lib.container.envList" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- range $env := $objectData.envList -}} + {{- if not $env.name -}} + {{- fail "Container - Expected non-empty " -}} + {{- end -}} {{/* Empty value is valid */}} + {{- include "ix.v1.common.helper.container.envDupeCheck" (dict "rootCtx" $rootCtx "objectData" $objectData "source" "envList" "key" $env.name) }} +- name: {{ $env.name | quote }} + value: {{ tpl (toString $env.value) $rootCtx | quote }} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/container/_fixedEnv.tpl b/library/common/1.0.0/templates/lib/container/_fixedEnv.tpl new file mode 100644 index 0000000000..fbafca6318 --- /dev/null +++ b/library/common/1.0.0/templates/lib/container/_fixedEnv.tpl @@ -0,0 +1,67 @@ +{{/* Returns Fixed Env */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.fixedEnv" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.lib.container.fixedEnv" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{/* Avoid nil pointers */}} + {{- if not (hasKey $objectData "fixedEnv") -}} + {{- $_ := set $objectData "fixedEnv" dict -}} + {{- end -}} + + {{- $nvidiaCaps := $rootCtx.Values.containerOptions.NVIDIA_CAPS -}} + + {{- if $objectData.fixedEnv.NVIDIA_CAPS -}} + {{- $nvidiaCaps = $objectData.fixedEnv.NVIDIA_CAPS -}} + {{- end -}} + + {{- if not (deepEqual $nvidiaCaps (mustUniq $nvidiaCaps)) -}} + {{- fail (printf "Container - Expected to have only unique values, but got [%s]" (join ", " $nvidiaCaps)) -}} + {{- end -}} + + {{- $caps := (list "all" "compute" "utility" "graphics" "video") -}} + {{- range $cap := $nvidiaCaps -}} + {{- if not (mustHas $cap $caps) -}} + {{- fail (printf "Container - Expected entry to be one of [%s], but got [%s]" (join ", " $caps) $cap) -}} + {{- end -}} + {{- end -}} + + {{- $secContext := fromJson (include "ix.v1.common.lib.container.securityContext.calculate" (dict "rootCtx" $rootCtx "objectData" $objectData)) -}} + + {{- $fixed := list -}} + {{- $TZ := $objectData.fixedEnv.TZ | default $rootCtx.Values.TZ -}} + {{- $UMASK := $objectData.fixedEnv.UMASK | default $rootCtx.Values.containerOptions.UMASK -}} + {{- $PUID := $objectData.fixedEnv.PUID | default $rootCtx.Values.containerOptions.PUID -}} + {{/* calculatedFSGroup is passed from the pod */}} + {{- $PGID := $objectData.calculatedFSGroup -}} + + {{- $fixed = mustAppend $fixed (dict "k" "TZ" "v" $TZ) -}} + {{- $fixed = mustAppend $fixed (dict "k" "UMASK" "v" $UMASK) -}} + {{- $fixed = mustAppend $fixed (dict "k" "UMASK_SET" "v" $UMASK) -}} + {{- if eq (include "ix.v1.common.lib.container.resources.gpu" (dict "rootCtx" $rootCtx "objectData" $objectData "returnBool" true)) "true" -}} + {{- $fixed = mustAppend $fixed (dict "k" "NVIDIA_DRIVER_CAPABILITIES" "v" (join "," $nvidiaCaps)) -}} + {{- end -}} + {{/* If running as root and PUID is set (0 or greater), set related envs */}} + {{- if and (or (eq (int $secContext.runAsUser) 0) (eq (int $secContext.runAsGroup) 0)) (ge (int $PUID) 0) -}} + {{- $fixed = mustAppend $fixed (dict "k" "PUID" "v" $PUID) -}} + {{- $fixed = mustAppend $fixed (dict "k" "USER_ID" "v" $PUID) -}} + {{- $fixed = mustAppend $fixed (dict "k" "UID" "v" $PUID) -}} + {{- $fixed = mustAppend $fixed (dict "k" "PGID" "v" $PGID) -}} + {{- $fixed = mustAppend $fixed (dict "k" "GROUP_ID" "v" $PGID) -}} + {{- $fixed = mustAppend $fixed (dict "k" "GID" "v" $PGID) -}} + {{- end -}} + {{/* If rootFS is readOnly OR does not as root, let s6 containers to know that fs is readonly */}} + {{- if or $secContext.readOnlyRootFilesystem $secContext.runAsNonRoot -}} + {{- $fixed = mustAppend $fixed (dict "k" "S6_READ_ONLY_ROOT" "v" "1") -}} + {{- end -}} + + {{- range $env := $fixed -}} + {{- include "ix.v1.common.helper.container.envDupeCheck" (dict "rootCtx" $rootCtx "objectData" $objectData "source" "fixedEnv" "key" $env.k) }} +- name: {{ $env.k | quote }} + value: {{ $env.v | quote }} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/container/_imageSelector.tpl b/library/common/1.0.0/templates/lib/container/_imageSelector.tpl new file mode 100644 index 0000000000..6e7f95dfac --- /dev/null +++ b/library/common/1.0.0/templates/lib/container/_imageSelector.tpl @@ -0,0 +1,42 @@ +{{/* Returns the image dictionary */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.imageSelector" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.lib.container.imageSelector" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $imageObj := dict -}} + + {{- $selector := "image" -}} + {{- with $objectData.imageSelector -}} + {{- $selector = tpl . $rootCtx -}} + {{- end -}} + + {{- if hasKey $rootCtx.Values $selector -}} + {{- $imageObj = get $rootCtx.Values $selector -}} + {{- else -}} + {{- fail (printf "Container - Expected <.Values.%s> to exist" $selector) -}} + {{- end -}} + + {{- if not $imageObj.repository -}} + {{- fail (printf "Container - Expected non-empty <.Values.%s.repository>" $selector) -}} + {{- end -}} + + {{- if not $imageObj.tag -}} + {{- fail (printf "Container - Expected non-empty <.Values.%s.tag>" $selector) -}} + {{- end -}} + + {{- if not $imageObj.pullPolicy -}} + {{- $_ := set $imageObj "pullPolicy" "IfNotPresent" -}} + {{- end -}} + + {{- $policies := (list "IfNotPresent" "Always" "Never") -}} + {{- if not (mustHas $imageObj.pullPolicy $policies) -}} + {{- fail (printf "Container - Expected <.Values.%s.pullPolicy> to be one of [%s], but got [%s]" $selector (join ", " $policies) $imageObj.pullPolicy) -}} + {{- end -}} + + {{- $imageObj | toJson -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/container/_lifecycle.tpl b/library/common/1.0.0/templates/lib/container/_lifecycle.tpl new file mode 100644 index 0000000000..e385c43fe1 --- /dev/null +++ b/library/common/1.0.0/templates/lib/container/_lifecycle.tpl @@ -0,0 +1,37 @@ +{{/* Returns lifecycle */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.lifecycle" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.lib.container.lifecycle" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $hooks := (list "preStop" "postStart") -}} + {{- $types := (list "exec" "http" "https") -}} + {{- with $objectData.lifecycle -}} + {{- range $hook, $hookValues := . -}} + {{- if not (mustHas $hook $hooks) -}} + {{- fail (printf "Container - Expected to be one of [%s], but got [%s]" (join ", " $hooks) $hook) -}} + {{- end -}} + + {{- if not $hookValues.type -}} + {{- fail "Container - Expected non-empty " -}} + {{- end -}} + + {{- if not (mustHas $hookValues.type $types) -}} + {{- fail (printf "Container - Expected to be one of [%s], but got [%s]" (join ", " $types) $hookValues.type) -}} + {{- end }} +{{ $hook }}: + {{- if eq $hookValues.type "exec" -}} + {{- include "ix.v1.common.lib.container.actions.exec" (dict "rootCtx" $rootCtx "objectData" $hookValues "caller" "lifecycle") | trim | nindent 2 -}} + {{- else if mustHas $hookValues.type (list "http" "https") -}} + {{- include "ix.v1.common.lib.container.actions.httpGet" (dict "rootCtx" $rootCtx "objectData" $hookValues "caller" "lifecycle") | trim | nindent 2 -}} + {{- end -}} + + {{- end -}} + {{- end -}} + + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/container/_ports.tpl b/library/common/1.0.0/templates/lib/container/_ports.tpl new file mode 100644 index 0000000000..e6a6cf10ae --- /dev/null +++ b/library/common/1.0.0/templates/lib/container/_ports.tpl @@ -0,0 +1,80 @@ +{{/* Returns ports list */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.ports" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.lib.container.ports" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- range $serviceName, $serviceValues := $rootCtx.Values.service -}} + {{- $podSelected := false -}} + {{/* If service is enabled... */}} + {{- if $serviceValues.enabled -}} + + {{/* If there is a selector */}} + {{- if $serviceValues.targetSelector -}} + + {{/* And pod is selected */}} + {{- if eq $serviceValues.targetSelector $objectData.podShortName -}} + {{- $podSelected = true -}} + {{- end -}} + + {{- else -}} + {{/* If no selector is defined but pod is primary */}} + {{- if $objectData.podPrimary -}} + {{- $podSelected = true -}} + {{- end -}} + + {{- end -}} + {{- end -}} + + {{- if $podSelected -}} + {{- range $portName, $portValues := $serviceValues.ports -}} + {{- $containerSelected := false -}} + + {{/* If service is enabled... */}} + {{- if $portValues.enabled -}} + {{/* If there is a selector */}} + {{- if $portValues.targetSelector -}} + + {{/* And container is selected */}} + {{- if eq $portValues.targetSelector $objectData.shortName -}} + {{- $containerSelected = true -}} + {{- end -}} + + {{- else -}} + {{/* If no selector is defined but contaienr is primary */}} + {{- if $objectData.primary -}} + {{- $containerSelected = true -}} + {{- end -}} + + {{- end -}} + {{- end -}} + + {{/* If the container is selected render port */}} + {{- if $containerSelected -}} + {{- $containerPort := $portValues.targetPort | default $portValues.port -}} + {{- if kindIs "string" $containerPort -}} + {{- $containerPort = (tpl $containerPort $rootCtx) -}} + {{- end -}} + + {{- $tcpProtocols := (list "tcp" "http" "https") -}} + {{- $protocol := tpl ($portValues.protocol | default $rootCtx.Values.fallbackDefaults.serviceProtocol) $rootCtx -}} + {{- if mustHas $protocol $tcpProtocols -}} + {{- $protocol = "tcp" -}} + {{- end }} +- name: {{ $portName }} + containerPort: {{ $containerPort }} + protocol: {{ $protocol | upper }} + {{- with $portValues.hostPort }} + hostPort: {{ . }} + {{- end -}} + {{- end -}} + + {{- end -}} + {{- end -}} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/container/_primaryValidation.tpl b/library/common/1.0.0/templates/lib/container/_primaryValidation.tpl new file mode 100644 index 0000000000..484edd2a01 --- /dev/null +++ b/library/common/1.0.0/templates/lib/container/_primaryValidation.tpl @@ -0,0 +1,40 @@ +{{/* Containers Basic Validation */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.primaryValidation" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} +*/}} +{{- define "ix.v1.common.lib.container.primaryValidation" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} + + {{/* Initialize values */}} + {{- $hasPrimary := false -}} + {{- $hasEnabled := false -}} + + {{/* Go over the contaienrs */}} + {{- range $name, $container := $objectData.podSpec.containers -}} + + {{/* If container is enabled */}} + {{- if $container.enabled -}} + {{- $hasEnabled = true -}} + + {{/* And container is primary */}} + {{- if and (hasKey $container "primary") ($container.primary) -}} + + {{/* Fail if there is already a primary container */}} + {{- if $hasPrimary -}} + {{- fail "Container - Only one container can be primary per workload" -}} + {{- end -}} + + {{- $hasPrimary = true -}} + + {{- end -}} + {{- end -}} + + {{- end -}} + + {{/* Require at least one primary container, if any enabled */}} + {{- if and $hasEnabled (not $hasPrimary) -}} + {{- fail "Container - At least one enabled container must be primary per workload" -}} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/container/_probes.tpl b/library/common/1.0.0/templates/lib/container/_probes.tpl new file mode 100644 index 0000000000..92fc187f89 --- /dev/null +++ b/library/common/1.0.0/templates/lib/container/_probes.tpl @@ -0,0 +1,98 @@ +{{/* Returns Probes */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.probes" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.lib.container.probes" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $probeNames := (list "liveness" "readiness" "startup") -}} + {{- $probeTypes := (list "http" "https" "tcp" "grpc" "exec") -}} + + {{- if not $objectData.probes -}} + {{- fail "Container - Expected non-empty " -}} + {{- end -}} + + {{- range $key := $probeNames -}} + {{- if not (get $objectData.probes $key) -}} + {{- fail (printf "Container - Expected to be defined" $key) -}} + {{- end -}} + {{- end -}} + + {{- range $probeName, $probe := $objectData.probes -}} + + {{- if not (mustHas $probeName $probeNames) -}} + {{- fail (printf "Container - Expected probe to be one of [%s], but got [%s]" (join ", " $probeNames) $probeName) -}} + {{- end -}} + + {{- $isEnabled := true -}} + {{- if kindIs "bool" $probe.enabled -}} + {{- $isEnabled = $probe.enabled -}} + {{- end -}} + + {{- if $isEnabled -}} + + {{- $probeType := $rootCtx.Values.fallbackDefaults.probeType -}} + + {{- with $probe.type -}} + {{- $probeType = tpl . $rootCtx -}} + {{- end -}} + + {{- if not (mustHas $probeType $probeTypes) -}} + {{- fail (printf "Container - Expected probe type to be one of [%s], but got [%s]" (join ", " $probeTypes) $probeType) -}} + {{- end }} +{{ $probeName }}Probe: + {{- if (mustHas $probeType (list "http" "https")) -}} + {{- include "ix.v1.common.lib.container.actions.httpGet" (dict "rootCtx" $rootCtx "objectData" $probe "caller" "probes") | trim | nindent 2 -}} + {{- else if eq $probeType "tcp" -}} + {{- include "ix.v1.common.lib.container.actions.tcpSocket" (dict "rootCtx" $rootCtx "objectData" $probe "caller" "probes") | trim | nindent 2 -}} + {{- else if eq $probeType "grpc" -}} + {{- include "ix.v1.common.lib.container.actions.grpc" (dict "rootCtx" $rootCtx "objectData" $probe "caller" "probes") | trim | nindent 2 -}} + {{- else if eq $probeType "exec" -}} + {{- include "ix.v1.common.lib.container.actions.exec" (dict "rootCtx" $rootCtx "objectData" $probe "caller" "probes") | trim | nindent 2 -}} + {{- end -}} + + {{- include "ix.v1.common.lib.container.probeTimeouts" (dict "rootCtx" $rootCtx "objectData" $probe "probeName" $probeName) | trim | nindent 2 -}} + + {{- end -}} + {{- end -}} +{{- end -}} + +{{/* Returns Probe Timeouts */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.probeTimeouts" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.lib.container.probeTimeouts" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + {{- $probeName := .probeName -}} + + {{- $timeouts := (get $rootCtx.Values.fallbackDefaults.probeTimeouts $probeName) -}} + + {{- if $objectData.spec -}} {{/* Overwrite with defined timeouts */}} + {{- $timeouts = mustMergeOverwrite $timeouts $objectData.spec -}} + {{- end -}} + + {{- $keys := (list "initialDelaySeconds" "failureThreshold" "successThreshold" "timeoutSeconds" "periodSeconds") -}} + {{- range $key := $keys -}} + {{- $number := get $timeouts $key -}} + {{- if not (mustHas (kindOf $number) (list "float64" "int")) -}} + {{- fail (printf "Container - Expected <%s> to be a number, but got [%s]" $key $number) -}} + {{- end -}} + {{- end -}} + + {{- if mustHas $probeName (list "liveness" "startup") -}} + {{- if ne (int $timeouts.successThreshold) 1 -}} + {{- fail (printf "Container - Expected to be 1 on [%s] probe" $probeName) -}} + {{- end -}} + {{- end }} +initialDelaySeconds: {{ $timeouts.initialDelaySeconds }} +failureThreshold: {{ $timeouts.failureThreshold }} +successThreshold: {{ $timeouts.successThreshold }} +timeoutSeconds: {{ $timeouts.timeoutSeconds }} +periodSeconds: {{ $timeouts.periodSeconds }} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/container/_resources.tpl b/library/common/1.0.0/templates/lib/container/_resources.tpl new file mode 100644 index 0000000000..ef6b00f4e1 --- /dev/null +++ b/library/common/1.0.0/templates/lib/container/_resources.tpl @@ -0,0 +1,138 @@ +{{/* Returns Resources */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.resources" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.lib.container.resources" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $resources := $rootCtx.Values.containerOptions.resources -}} + + {{- if $objectData.resources -}} + {{- $resources = mustMergeOverwrite $resources $objectData.resources -}} + {{- end -}} + + {{- include "ix.v1.common.lib.container.resources.validation" (dict "resources" $resources) -}} + +requests: + cpu: {{ $resources.requests.cpu }} + memory: {{ $resources.requests.memory }} + {{- if $resources.limits }} +limits: + {{- with $resources.limits.cpu }} {{/* Passing 0, will not render it, meaning unlimited */}} + cpu: {{ . }} + {{- end -}} + {{- with $resources.limits.memory }} {{/* Passing 0, will not render it, meaning unlimited */}} + memory: {{ . }} + {{- end -}} + {{- include "ix.v1.common.lib.container.resources.gpu" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 2 -}} + {{- end -}} +{{- end -}} + +{{/* Returns GPU resource */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.resources.gpu" (dict "rootCtx" $rootCtx "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.lib.container.resources.gpu" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} + {{- $returnBool := .returnBool -}} + + {{- $gpuResource := list -}} + + {{- range $GPUValues := $rootCtx.Values.scaleGPU -}} + {{- if not $GPUValues.gpu -}} + {{- fail "Container - Expected non-empty " -}} + {{- end -}} + + {{- $selected := false -}} + + {{/* Parse selector if defined */}} + {{- if $GPUValues.targetSelector -}} + {{- range $podName, $containers := $GPUValues.targetSelector -}} + {{- if not $containers -}} + {{- fail "Container - Expected non-empty list under pod in " -}} + {{- end -}} + + {{- if and (eq $podName $objectData.podShortName) (mustHas $objectData.shortName $containers) -}} + {{- $selected = true -}} + {{- end -}} + {{- end -}} + {{/* If no selector, select primary pod/container */}} + {{- else if and $objectData.podPrimary $objectData.primary -}} + {{- $selected = true -}} + {{- end -}} + + {{- if $selected -}} + {{- $gpuResource = mustAppend $gpuResource $GPUValues.gpu -}} + {{- end -}} + {{- end -}} + + {{- if not $returnBool -}} + {{- range $gpu := $gpuResource -}} + {{- range $k, $v := $gpu -}} + {{- if not $v -}} + {{- fail "Container - Expected non-empty " -}} + {{- end }} +{{ $k }}: {{ $v | quote }} + {{- end -}} + {{- end -}} + {{- else -}} + {{- if $gpuResource -}} + {{- "true" -}} + {{- end -}} + {{- end -}} + +{{- end -}} + +{{/* Validates resources to match a pattern */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.resources.validation" (dict "resources" $resources) }} +rootCtx: The root context of the chart. +resources: The resources object +*/}} +{{- define "ix.v1.common.lib.container.resources.validation" -}} + {{- $resources := .resources -}} + {{/* CPU: https://regex101.com/r/D4HouI/1 */}} + {{/* MEM: https://regex101.com/r/NNPV2D/1 */}} + {{- $regex := (dict + "cpu" "^(0\\.[1-9]|[1-9][0-9]*)(\\.[0-9]|m?)$" + "memory" "^[1-9][0-9]*([EPTGMK]i?|e[0-9]+)?$") -}} + {{- $errorMsg := (dict + "cpu" "(Plain Integer - eg. 1), (Float - eg. 0.5), (Milicpu - eg. 500m)" + "memory" "(Suffixed with E/P/T/G/M/K - eg. 1G), (Suffixed with Ei/Pi/Ti/Gi/Mi/Ki - eg. 1Gi), (Plain Integer in bytes - eg. 1024), (Exponent - eg. 134e6)") -}} + + {{- $resourceTypes := (list "cpu" "memory") -}} + + {{- range $category := (list "requests") -}} {{/* We can also add "limits" here if we want to require them */}} + {{- if not (get $resources $category) -}} + {{- fail (printf "Container - Expected non-empty " $category) -}} + {{- end -}} + + {{- range $type := $resourceTypes -}} + {{- if not (get (get $resources $category) $type) -}} + {{- fail (printf "Container - Expected non-empty " $category $type) -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- range $key := (list "requests" "limits") -}} + {{- $resourceCategory := (get $resources $key) -}} + {{- if $resourceCategory -}} + + {{- range $type := $resourceTypes -}} + {{- $resourceValue := (get $resourceCategory $type) -}} + {{- if $resourceValue -}} {{/* Only try to match defined values */}} + {{- if not (mustRegexMatch (get $regex $type) (toString $resourceValue)) -}} + {{- fail (printf "Container - Expected to have one of the following formats [%s], but got [%s]" $key $type (get $errorMsg $type) $resourceValue) -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/container/_securityContext.tpl b/library/common/1.0.0/templates/lib/container/_securityContext.tpl new file mode 100644 index 0000000000..60088ff81e --- /dev/null +++ b/library/common/1.0.0/templates/lib/container/_securityContext.tpl @@ -0,0 +1,119 @@ +{{/* Returns Container Security Context */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.securityContext" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.lib.container.securityContext" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{/* Initialize from the "global" options */}} + {{- $secContext := fromJson (include "ix.v1.common.lib.container.securityContext.calculate" (dict "rootCtx" $rootCtx "objectData" $objectData)) }} +runAsNonRoot: {{ $secContext.runAsNonRoot }} +runAsUser: {{ $secContext.runAsUser }} +runAsGroup: {{ $secContext.runAsGroup }} +readOnlyRootFilesystem: {{ $secContext.readOnlyRootFilesystem }} +allowPrivilegeEscalation: {{ $secContext.allowPrivilegeEscalation }} +privileged: {{ $secContext.privileged }} +seccompProfile: + type: {{ $secContext.seccompProfile.type }} + {{- if eq $secContext.seccompProfile.type "Localhost" }} + localhostProfile: {{ $secContext.seccompProfile.profile }} + {{- end }} +capabilities: + {{- if $secContext.capabilities.add }} + add: + {{- range $secContext.capabilities.add }} + - {{ . }} + {{- end -}} + {{- else }} + add: [] + {{- end -}} + {{- if $secContext.capabilities.drop }} + drop: + {{- range $secContext.capabilities.drop }} + - {{ . }} + {{- end -}} + {{- else }} + drop: [] + {{- end -}} +{{- end -}} + +{{/* Calculates Container Security Context */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.securityContext.calculate" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.lib.container.securityContext.calculate" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- if not $rootCtx.Values.securityContext.container -}} + {{- fail "Container - Expected non-empty <.Values.securityContext.container>" -}} + {{- end -}} + + {{/* Initialize from the "global" options */}} + {{- $secContext := mustDeepCopy $rootCtx.Values.securityContext.container -}} + + {{/* Override with container's options */}} + {{- with $objectData.securityContext -}} + {{- $secContext = mustMergeOverwrite $secContext . -}} + {{- end -}} + + {{/* Validations, as we might endup with null values after merge */}} + {{- range $key := (list "privileged" "allowPrivilegeEscalation" "runAsNonRoot" "readOnlyRootFilesystem") -}} + {{- $value := (get $secContext $key) -}} + {{- if not (kindIs "bool" $value) -}} + {{- fail (printf "Container - Expected to be [bool], but got [%s] of type [%s]" $key $value (kindOf $value)) -}} + {{- end -}} + {{- end -}} + + {{- range $key := (list "runAsUser" "runAsGroup") -}} + {{- $value := (get $secContext $key) -}} + {{- if not (mustHas (kindOf $value) (list "float64" "int")) -}} + {{- fail (printf "Container - Expected to be [int], but got [%s] of type [%s]" $key $value (kindOf $value)) -}} + {{- end -}} + {{- end -}} + + {{- if not $secContext.seccompProfile -}} + {{- fail "Container - Expected to be defined" -}} + {{- end -}} + + {{- $profiles := (list "RuntimeDefault" "Localhost" "Unconfined") -}} + {{- if not (mustHas $secContext.seccompProfile.type $profiles) -}} + {{- fail (printf "Container - Expected to be one of [%s], but got [%s]" (join ", " $profiles) $secContext.seccompProfile.type) -}} + {{- end -}} + + {{- if eq $secContext.seccompProfile.type "Localhost" -}} + {{- if not $secContext.seccompProfile.profile -}} + {{- fail "Container - Expected to be defined on type [Localhost]" -}} + {{- end -}} + {{- end -}} + + {{- if not $secContext.capabilities -}} + {{- fail "Container - Expected to be defined" -}} + {{- end -}} + + {{- range $key := (list "add" "drop") -}} + {{- $item := (get $secContext.capabilities $key) -}} + {{- if not (kindIs "slice" $item) -}} + {{- fail (printf "Container - Expected to be [list], but got [%s]" $key (kindOf $item)) -}} + {{- end -}} + + {{- range $item -}} + {{- if not (kindIs "string" .) -}} + {{- fail (printf "Container - Expected items of to be [string], but got [%s]" $key (kindOf .)) -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- if or (eq (int $secContext.runAsUser) 0) (eq (int $secContext.runAsGroup) 0) -}} + {{- if $secContext.runAsNonRoot -}} + {{- fail "Container - Expected to be [false] with either [runAsUser, runAsGroup] set to [0]" -}} + {{- end -}} + {{- end -}} + + {{- $secContext | toJson -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/container/_termination.tpl b/library/common/1.0.0/templates/lib/container/_termination.tpl new file mode 100644 index 0000000000..d689a65907 --- /dev/null +++ b/library/common/1.0.0/templates/lib/container/_termination.tpl @@ -0,0 +1,33 @@ +{{/* Returns termination */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.termination" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.lib.container.termination" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $termination := (dict "messagePath" "" "messagePolicy" "") -}} + + {{- with $objectData.termination -}} + {{- with .messagePath -}} + {{- $_ := set $termination "messagePath" (tpl . $rootCtx) -}} + {{- end -}} + + {{- with .messagePolicy -}} + + {{- $policy := (tpl . $rootCtx) -}} + + {{- $policies := (list "File" "FallbackToLogsOnError") -}} + {{- if not (mustHas $policy $policies) -}} + {{- fail (printf "Container - Expected to be one of [%s], but got [%s]" (join ", " $policies) $policy) -}} + {{- end -}} + + {{- $_ := set $termination "messagePolicy" $policy -}} + {{- end -}} + + {{- end -}} + + {{- $termination | toJson -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/container/_volumeMounts.tpl b/library/common/1.0.0/templates/lib/container/_volumeMounts.tpl new file mode 100644 index 0000000000..404d77a4f9 --- /dev/null +++ b/library/common/1.0.0/templates/lib/container/_volumeMounts.tpl @@ -0,0 +1,95 @@ +{{/* Returns volumeMount list */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.volumeMount" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.lib.container.volumeMount" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $volMounts := list -}} + + {{- $keys := (list "persistence") -}} + + {{- range $key := $keys -}} + {{- range $persistenceName, $persistenceValues := (get $rootCtx.Values $key) -}} + + {{/* Initialize from the default values */}} + {{- $volMount := dict -}} + {{- $_ := set $volMount "name" $persistenceName -}} + {{- $_ := set $volMount "key" $key -}} + {{- $_ := set $volMount "mountPath" ($persistenceValues.mountPath | default "") -}} + {{- $_ := set $volMount "subPath" ($persistenceValues.subPath | default "") -}} + {{- $_ := set $volMount "readOnly" ($persistenceValues.readOnly | default false) -}} + {{- $_ := set $volMount "mountPropagation" ($persistenceValues.mountPropagation | default "") -}} + + {{/* If persistence is enabled... */}} + {{- if $persistenceValues.enabled -}} + {{/* If targetSelectAll is set, means all pods/containers */}} {{/* targetSelectAll does not make sense for vct */}} + {{- if and $persistenceValues.targetSelectAll -}} + {{- $volMounts = mustAppend $volMounts $volMount -}} + + {{/* Else if selector is defined */}} + {{- else if $persistenceValues.targetSelector -}} + {{/* If pod is selected */}} + {{- if mustHas $objectData.podShortName ($persistenceValues.targetSelector | keys) -}} + {{- $selectorValues := (get $persistenceValues.targetSelector $objectData.podShortName) -}} + {{- if not (kindIs "map" $selectorValues) -}} + {{- fail (printf "%s - Expected to be a [dict], but got [%s]" (camelcase $key) $objectData.podShortName (kindOf $selectorValues)) -}} + {{- end -}} + + {{- if not $selectorValues -}} + {{- fail (printf "%s - Expected non-empty " (camelcase $key) $objectData.podShortName) -}} + {{- end -}} + + {{/* If container is selected */}} + {{- if mustHas $objectData.shortName ($selectorValues | keys) -}} + {{/* Merge with values that might be set for the specific container */}} + {{- $volMount = mustMergeOverwrite $volMount (get $selectorValues $objectData.shortName) -}} + {{- $volMounts = mustAppend $volMounts $volMount -}} + {{- end -}} + {{- end -}} + + {{/* Else if not selector, but pod and container is primary */}} + {{- else if and $objectData.podPrimary $objectData.primary -}} + {{- $volMounts = mustAppend $volMounts $volMount -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- range $volMount := $volMounts -}} + {{/* Expand values */}} + {{- $_ := set $volMount "mountPath" (tpl $volMount.mountPath $rootCtx) -}} + {{- $_ := set $volMount "subPath" (tpl $volMount.subPath $rootCtx) -}} + {{- $_ := set $volMount "mountPropagation" (tpl $volMount.mountPropagation $rootCtx) -}} + + {{- if not $volMount.mountPath -}} + {{- fail (printf "%s - Expected non-empty " (camelcase $volMount.key)) -}} + {{- end -}} + + {{- if not (hasPrefix "/" $volMount.mountPath) -}} + {{- fail (printf "%s - Expected to start with a forward slash [/]" (camelcase $volMount.key)) -}} + {{- end -}} + + {{- $propagationTypes := (list "None" "HostToContainer" "Bidirectional") -}} + {{- if and $volMount.mountPropagation (not (mustHas $volMount.mountPropagation $propagationTypes)) -}} + {{- fail (printf "%s - Expected to be one of [%s], but got [%s]" (camelcase $volMount.key) (join ", " $propagationTypes) $volMount.mountPropagation) -}} + {{- end -}} + + {{- if not (kindIs "bool" $volMount.readOnly) -}} + {{- fail (printf "%s - Expected to be [boolean], but got [%s]" (camelcase $volMount.key) (kindOf $volMount.readOnly)) -}} + {{- end }} +- name: {{ $volMount.name }} + mountPath: {{ $volMount.mountPath }} + readOnly: {{ $volMount.readOnly }} + {{- with $volMount.subPath }} + subPath: {{ . }} + {{- end -}} + {{- with $volMount.mountPropagation }} + mountPropagation: {{ . }} + {{- end -}} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/container/probe-lifecycle-actions/_exec.tpl b/library/common/1.0.0/templates/lib/container/probe-lifecycle-actions/_exec.tpl new file mode 100644 index 0000000000..89b0ba2520 --- /dev/null +++ b/library/common/1.0.0/templates/lib/container/probe-lifecycle-actions/_exec.tpl @@ -0,0 +1,18 @@ +{{/* Returns exec action */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.actions.exec" (dict "rootCtx" $ "objectData" $objectData "caller" $caller) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.lib.container.actions.exec" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + {{- $caller := .caller -}} + + {{- if not $objectData.command -}} + {{- fail (printf "Container - Expected non-empty <%s> on [exec] type" $caller) -}} + {{- end }} +exec: + command: + {{- include "ix.v1.common.lib.container.command" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 4}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/container/probe-lifecycle-actions/_grpc.tpl b/library/common/1.0.0/templates/lib/container/probe-lifecycle-actions/_grpc.tpl new file mode 100644 index 0000000000..905e1993d8 --- /dev/null +++ b/library/common/1.0.0/templates/lib/container/probe-lifecycle-actions/_grpc.tpl @@ -0,0 +1,23 @@ +{{/* Returns grpc action */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.actions.tcpSocket" (dict "rootCtx" $ "objectData" $objectData "caller" $caller) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.lib.container.actions.grpc" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + {{- $caller := .caller -}} + + {{- if not $objectData.port -}} + {{- fail (printf "Container - Expected non-empty <%s> on [grpc] type" $caller) -}} + {{- end -}} + + {{- $port := $objectData.port -}} + + {{- if kindIs "string" $port -}} + {{- $port = tpl $port $rootCtx -}} + {{- end }} +grpc: + port: {{ $port }} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/container/probe-lifecycle-actions/_httpGet.tpl b/library/common/1.0.0/templates/lib/container/probe-lifecycle-actions/_httpGet.tpl new file mode 100644 index 0000000000..0a782c2695 --- /dev/null +++ b/library/common/1.0.0/templates/lib/container/probe-lifecycle-actions/_httpGet.tpl @@ -0,0 +1,53 @@ +{{/* Returns httpGet action */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.actions.httpGet" (dict "rootCtx" $ "objectData" $objectData "caller" $caller) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.lib.container.actions.httpGet" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + {{- $caller := .caller -}} + + {{- if not $objectData.port -}} + {{- fail (printf "Container - Expected non-empty <%s> on [http] type" $caller) -}} + {{- end -}} + + {{- $port := $objectData.port -}} + {{- $path := "/" -}} + {{- $scheme := "http" -}} + + {{- if kindIs "string" $port -}} + {{- $port = tpl $port $rootCtx -}} + {{- end -}} + + {{- with $objectData.path -}} + {{- $path = tpl . $rootCtx -}} + {{- end -}} + + {{- if not (hasPrefix "/" $path) -}} + {{- fail (printf "Container - Expected <%s> to start with a forward slash [/] on type" $caller) -}} + {{- end -}} + + {{- with $objectData.type -}} + {{- $scheme = tpl . $rootCtx -}} + {{- end }} +httpGet: + {{- with $objectData.host }} + host: {{ tpl . $rootCtx }} + {{- end }} + port: {{ $port }} + path: {{ $path }} + scheme: {{ $scheme | upper }} + {{- with $objectData.httpHeaders }} + httpHeaders: + {{- range $name, $value := . }} + {{- if not $value -}} + {{- fail "Container - Expected non-empty on " -}} + {{- end }} + - name: {{ $name }} + value: {{ tpl (toString $value) $rootCtx | quote }} + {{- end -}} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/container/probe-lifecycle-actions/_tcpSocket.tpl b/library/common/1.0.0/templates/lib/container/probe-lifecycle-actions/_tcpSocket.tpl new file mode 100644 index 0000000000..2d6c9aacc9 --- /dev/null +++ b/library/common/1.0.0/templates/lib/container/probe-lifecycle-actions/_tcpSocket.tpl @@ -0,0 +1,23 @@ +{{/* Returns tcpSocket action */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.container.actions.tcpSocket" (dict "rootCtx" $ "objectData" $objectData "caller" $caller) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the container. +*/}} +{{- define "ix.v1.common.lib.container.actions.tcpSocket" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + {{- $caller := .caller -}} + + {{- if not $objectData.port -}} + {{- fail (printf "Container - Expected non-empty <%s> on [tcp] type" $caller) -}} + {{- end -}} + + {{- $port := $objectData.port -}} + + {{- if kindIs "string" $port -}} + {{- $port = tpl $port $rootCtx -}} + {{- end }} +tcpSocket: + port: {{ $port }} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/externalInterface/_validation.tpl b/library/common/1.0.0/templates/lib/externalInterface/_validation.tpl new file mode 100644 index 0000000000..d4f284f624 --- /dev/null +++ b/library/common/1.0.0/templates/lib/externalInterface/_validation.tpl @@ -0,0 +1,53 @@ +{{/* External Interface Validation */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.externalInterface.validation" (dict "objectData" $objectData) -}} +objectData: The object data to validate that contains the external interface configuratioon. +*/}} + +{{- define "ix.v1.common.lib.externalInterface.validation" -}} + {{- $objectData := .objectData -}} + + {{- if and $objectData.targetSelector (not (kindIs "slice" $objectData.targetSelector)) -}} + {{- fail (printf "External Interface - Expected to be a [list], but got [%s]" (kindOf $objectData.targetSelector)) -}} + {{- end -}} + + {{- if not $objectData.hostInterface -}} + {{- fail "External Interface - Expected non-empty " -}} + {{- end -}} + + {{- if not $objectData.ipam -}} + {{- fail "External Interface - Expected non-empty " -}} + {{- end -}} + + {{- if not $objectData.ipam.type -}} + {{- fail "External Interface - Expected non-empty " -}} + {{- end -}} + + {{- $types := (list "dhcp" "static") -}} + {{- if not (mustHas $objectData.ipam.type $types) -}} + {{- fail (printf "External Interface - Expected to be one of [%s], but got [%s]" (join ", " $types) $objectData.ipam.type) -}} + {{- end -}} + + {{- if and (or $objectData.staticIPConfigurations $objectData.staticRoutes) (ne $objectData.ipam.type "static") -}} + {{- fail "External Interface - Expected empty and when is not [static]" -}} + {{- end -}} + + {{- if eq $objectData.ipam.type "static" -}} + {{- if not $objectData.staticIPConfigurations -}} + {{- fail "External Interface - Expected non-empty when is [static]" -}} + {{- end -}} + + {{- with $objectData.staticRoutes -}} + {{- range . -}} + {{- if not .destination -}} + {{- fail "External Interface - Expected non-empty in " -}} + {{- end -}} + + {{- if not .gateway -}} + {{- fail "External Interface - Expected non-empty in " -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/imagePullSecret/_createData.tpl b/library/common/1.0.0/templates/lib/imagePullSecret/_createData.tpl new file mode 100644 index 0000000000..b1f3b8224e --- /dev/null +++ b/library/common/1.0.0/templates/lib/imagePullSecret/_createData.tpl @@ -0,0 +1,42 @@ +{{/* Configmap Validation */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.imagePullSecret.createData" (dict "objectData" $objectData "root" $rootCtx) -}} +rootCtx: The root context of the chart. +objectData: + data: The data of the imagePullSecret. +*/}} + +{{- define "ix.v1.common.lib.imagePullSecret.createData" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} + + {{- $registrySecret := dict -}} + + {{/* Auth is b64encoded and then the whole secret is b64encoded */}} + {{- $auth := printf "%s:%s" (tpl $objectData.data.username $rootCtx) (tpl $objectData.data.password $rootCtx) | b64enc -}} + + {{- $registry := dict -}} + {{- with $objectData.data -}} + {{- $registry = (dict "username" (tpl .username $rootCtx) "password" (tpl .password $rootCtx) + "email" (tpl .email $rootCtx) "auth" $auth) -}} + {{- end -}} + + {{- $_ := set $registrySecret "auths" (dict "registry" $registry) -}} + + {{/* + This should result in something like this: + { + "auths": { + "$registry": { + "username": "$username", + "password": "$password", + "email": "$email", + "auth": "($username:$password) base64" + } + } +} +*/}} + + {{/* Return the registrySecret as Json */}} + {{- $registrySecret | toJson -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/imagePullSecret/_validation.tpl b/library/common/1.0.0/templates/lib/imagePullSecret/_validation.tpl new file mode 100644 index 0000000000..80328622e1 --- /dev/null +++ b/library/common/1.0.0/templates/lib/imagePullSecret/_validation.tpl @@ -0,0 +1,27 @@ +{{/* Configmap Validation */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.imagePullSecret.validation" (dict "objectData" $objectData) -}} +objectData: + labels: The labels of the imagePullSecret. + annotations: The annotations of the imagePullSecret. + data: The data of the imagePullSecret. +*/}} + +{{- define "ix.v1.common.lib.imagePullSecret.validation" -}} + {{- $objectData := .objectData -}} + + {{- if not $objectData.data -}} + {{- fail "Image Pull Secret - Expected non-empty " -}} + {{- end -}} + + {{- if not (kindIs "map" $objectData.data) -}} + {{- fail (printf "Image Pull Secret - Expected to be a dictionary, but got [%v]" (kindOf $objectData.data)) -}} + {{- end -}} + + {{- range $key := (list "username" "password" "registry" "email") -}} + {{- if not (get $objectData.data $key) -}} + {{- fail (printf "Image Pull Secret - Expected non-empty <%s>" $key) -}} + {{- end -}} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/metadata/_allAnnotations.tpl b/library/common/1.0.0/templates/lib/metadata/_allAnnotations.tpl new file mode 100644 index 0000000000..0a2d6ddfea --- /dev/null +++ b/library/common/1.0.0/templates/lib/metadata/_allAnnotations.tpl @@ -0,0 +1,9 @@ +{{/* Annotations that are added to all objects */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.metadata.allAnnotations" $ }} +*/}} +{{- define "ix.v1.common.lib.metadata.allAnnotations" -}} + {{/* Currently empty but can add later, if needed */}} +{{- include "ix.v1.common.lib.metadata.globalAnnotations" . }} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/metadata/_allLabels.tpl b/library/common/1.0.0/templates/lib/metadata/_allLabels.tpl new file mode 100644 index 0000000000..eceea568c9 --- /dev/null +++ b/library/common/1.0.0/templates/lib/metadata/_allLabels.tpl @@ -0,0 +1,13 @@ +{{/* Labels that are added to all objects */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.metadata.allLabels" $ }} +*/}} +{{- define "ix.v1.common.lib.metadata.allLabels" -}} +helm.sh/chart: {{ include "ix.v1.common.lib.chart.names.chart" . }} +helm-revision: {{ .Release.Revision | quote }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app: {{ include "ix.v1.common.lib.chart.names.chart" . }} +release: {{ .Release.Name }} +{{- include "ix.v1.common.lib.metadata.globalLabels" . }} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/metadata/_externalInterfaceAnnotations.tpl b/library/common/1.0.0/templates/lib/metadata/_externalInterfaceAnnotations.tpl new file mode 100644 index 0000000000..62ce550714 --- /dev/null +++ b/library/common/1.0.0/templates/lib/metadata/_externalInterfaceAnnotations.tpl @@ -0,0 +1,52 @@ + + +{{/* External Interface Annotations that are added to podSpec */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.metadata.externalInterfacePodAnnotations" (dict "rootCtx" $ "podShortName" $podShortName) }} +rootCtx is the root context of the chart +objectData is object containing the data of the pod +*/}} +{{- define "ix.v1.common.lib.metadata.externalInterfacePodAnnotations" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} + + {{- $ifaceIndexes := list -}} + + {{- range $index, $iface := $rootCtx.Values.scaleExternalInterface -}} + {{/* If targetSelectAll is set append the index */}} + {{- if .targetSelectAll -}} + {{- $ifaceIndexes = mustAppend $ifaceIndexes $index -}} + {{/* Else If targetSelector is set and pod is selected append the index */}} + {{- else if and .targetSelector (mustHas $objectData.shortName .targetSelector) -}} + {{- $ifaceIndexes = mustAppend $ifaceIndexes $index -}} + {{/* Else If none of the above, but pod is primary append the index */}} + {{- else if $objectData.primary -}} + {{- $ifaceIndexes = mustAppend $ifaceIndexes $index -}} + {{- end -}} + {{- end -}} + + {{- $ifaceNames := list -}} + {{- if $rootCtx.Values.ixExternalInterfacesConfiguration -}} + {{- with $rootCtx.Values.ixExternalInterfacesConfigurationNames -}} + {{- range $ifaceName := . -}} + {{/* Get the index by splitting the iFaceName (ix-RELEASE-NAME-0) */}} + {{- $index := splitList "-" $ifaceName -}} + {{/* And pick the last item on the list */}} + {{- $index = mustLast $index -}} + + {{/* If the index is in the list of indexes to be added, append the name */}} + {{- if mustHas (int $index) $ifaceIndexes -}} + {{- $ifaceNames = mustAppend $ifaceNames $ifaceName -}} + {{- end -}} + + {{- end -}} + {{- else -}} + {{- fail "External Interface - Expected non empty " -}} + {{- end -}} + {{- end -}} + + {{/* If we have ifaceNames, then add the annotations to the pod calling this template */}} + {{- if $ifaceNames }} +k8s.v1.cni.cncf.io/networks: {{ join ", " $ifaceNames }} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/metadata/_globalAnnotations.tpl b/library/common/1.0.0/templates/lib/metadata/_globalAnnotations.tpl new file mode 100644 index 0000000000..c05c24bc79 --- /dev/null +++ b/library/common/1.0.0/templates/lib/metadata/_globalAnnotations.tpl @@ -0,0 +1,6 @@ +{{/* Returns the global annotations */}} +{{- define "ix.v1.common.lib.metadata.globalAnnotations" -}} + + {{- include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $ "annotations" .Values.global.annotations) -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/metadata/_globalLabels.tpl b/library/common/1.0.0/templates/lib/metadata/_globalLabels.tpl new file mode 100644 index 0000000000..18da33a798 --- /dev/null +++ b/library/common/1.0.0/templates/lib/metadata/_globalLabels.tpl @@ -0,0 +1,6 @@ +{{/* Returns the global labels */}} +{{- define "ix.v1.common.lib.metadata.globalLabels" -}} + + {{- include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $ "labels" .Values.global.labels) -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/metadata/_podAnnotations.tpl b/library/common/1.0.0/templates/lib/metadata/_podAnnotations.tpl new file mode 100644 index 0000000000..6c2120bc89 --- /dev/null +++ b/library/common/1.0.0/templates/lib/metadata/_podAnnotations.tpl @@ -0,0 +1,7 @@ +{{/* Annotations that are added to podSpec */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.metadata.podAnnotations" $ }} +*/}} +{{- define "ix.v1.common.lib.metadata.podAnnotations" -}} +rollme: {{ randAlphaNum 5 | quote }} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/metadata/_podLabels.tpl b/library/common/1.0.0/templates/lib/metadata/_podLabels.tpl new file mode 100644 index 0000000000..f8f781563e --- /dev/null +++ b/library/common/1.0.0/templates/lib/metadata/_podLabels.tpl @@ -0,0 +1,6 @@ +{{/* Labels that are added to podSpec */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.metadata.podLabels" $ }} +*/}} +{{- define "ix.v1.common.lib.metadata.podLabels" -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/metadata/_render.tpl b/library/common/1.0.0/templates/lib/metadata/_render.tpl new file mode 100644 index 0000000000..842466f5af --- /dev/null +++ b/library/common/1.0.0/templates/lib/metadata/_render.tpl @@ -0,0 +1,28 @@ +{{/* Renders a dict of labels */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $ "labels" $labels) }} +{{ include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $ "annotations" $annotations) }} +*/}} + +{{- define "ix.v1.common.lib.metadata.render" -}} + {{- $labels := .labels -}} + {{- $annotations := .annotations -}} + {{- $rootCtx := .rootCtx -}} + + {{- with $labels -}} + {{- range $k, $v := . -}} + {{- if and $k $v }} +{{ $k }}: {{ tpl $v $rootCtx | quote }} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- with $annotations -}} + {{- range $k, $v := . -}} + {{- if and $k $v }} +{{ $k }}: {{ tpl $v $rootCtx | quote }} + {{- end -}} + {{- end -}} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/metadata/_selectorLabels.tpl b/library/common/1.0.0/templates/lib/metadata/_selectorLabels.tpl new file mode 100644 index 0000000000..a965d2e70b --- /dev/null +++ b/library/common/1.0.0/templates/lib/metadata/_selectorLabels.tpl @@ -0,0 +1,16 @@ +{{/* Labels that are used on selectors */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.metadata.selectorLabels" (dict "rootCtx" $rootCtx "podName" $podName) }} +podName is the "shortName" of the pod. The one you define in the .Values.workload +*/}} +{{- define "ix.v1.common.lib.metadata.selectorLabels" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectType := .objectType -}} + {{- $objectName := .objectName }} + +{{- if $objectType }} +{{ printf "%s.name" $objectType }}: {{ $objectName }} +{{- end }} +app.kubernetes.io/name: {{ include "ix.v1.common.lib.chart.names.name" $rootCtx }} +app.kubernetes.io/instance: {{ $rootCtx.Release.Name }} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/metadata/_validation.tpl b/library/common/1.0.0/templates/lib/metadata/_validation.tpl new file mode 100644 index 0000000000..4930f95ed2 --- /dev/null +++ b/library/common/1.0.0/templates/lib/metadata/_validation.tpl @@ -0,0 +1,22 @@ +{{/* Metadata Validation */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.metadata.validation" (dict "objectData" $objectData "caller" $caller) -}} +objectData: + labels: The labels of the configmap. + annotations: The annotations of the configmap. + data: The data of the configmap. +*/}} + +{{- define "ix.v1.common.lib.metadata.validation" -}} + {{- $objectData := .objectData -}} + {{- $caller := .caller -}} + + {{- if and $objectData.labels (not (kindIs "map" $objectData.labels)) -}} + {{- fail (printf "%s - Expected to be a dictionary, but got [%v]" $caller (kindOf $objectData.labels)) -}} + {{- end -}} + + {{- if and $objectData.annotations (not (kindIs "map" $objectData.annotations)) -}} + {{- fail (printf "%s - Expected to be a dictionary, but got [%v]" $caller (kindOf $objectData.annotations)) -}} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/pod/_autoMountServiceAccountToken.tpl b/library/common/1.0.0/templates/lib/pod/_autoMountServiceAccountToken.tpl new file mode 100644 index 0000000000..03abbfd681 --- /dev/null +++ b/library/common/1.0.0/templates/lib/pod/_autoMountServiceAccountToken.tpl @@ -0,0 +1,24 @@ +{{/* Returns automountServiceAccountToken */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.pod.automountServiceAccountToken" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the Pod. +*/}} +{{- define "ix.v1.common.lib.pod.automountServiceAccountToken" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $automount := false -}} + + {{/* Initialize from the "global" option */}} + {{- if (kindIs "bool" $rootCtx.Values.podOptions.automountServiceAccountToken) -}} + {{- $automount = $rootCtx.Values.podOptions.automountServiceAccountToken -}} + {{- end -}} + + {{/* Override with pod's option */}} + {{- if (kindIs "bool" $objectData.podSpec.automountServiceAccountToken) -}} + {{- $automount = $objectData.podSpec.automountServiceAccountToken -}} + {{- end -}} + + {{- $automount -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/pod/_container.tpl b/library/common/1.0.0/templates/lib/pod/_container.tpl new file mode 100644 index 0000000000..52c678f05c --- /dev/null +++ b/library/common/1.0.0/templates/lib/pod/_container.tpl @@ -0,0 +1,62 @@ +{{/* Returns Container */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.pod.container" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the Pod. +*/}} +{{- define "ix.v1.common.lib.pod.container" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $imageObj := fromJson (include "ix.v1.common.lib.container.imageSelector" (dict "rootCtx" $rootCtx "objectData" $objectData)) -}} + {{- $termination := fromJson (include "ix.v1.common.lib.container.termination" (dict "rootCtx" $rootCtx "objectData" $objectData)) }} +- name: {{ $objectData.name }} + image: {{ printf "%s:%s" $imageObj.repository $imageObj.tag }} + imagePullPolicy: {{ $imageObj.pullPolicy }} + tty: {{ $objectData.tty | default false }} + stdin: {{ $objectData.stdin | default false }} + {{- with (include "ix.v1.common.lib.container.command" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim) }} + command: + {{- . | nindent 4 }} + {{- end -}} + {{- with (include "ix.v1.common.lib.container.args" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim) }} + args: + {{- . | nindent 4 }} + {{- end -}} + {{- with $termination.messagePath }} + terminationMessagePath: {{ . }} + {{- end -}} + {{- with $termination.messagePolicy }} + terminationMessagePolicy: {{ . }} + {{- end -}} + {{- with (include "ix.v1.common.lib.container.lifecycle" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim) }} + lifecycle: + {{- . | nindent 4 }} + {{- end -}} + {{- with (include "ix.v1.common.lib.container.ports" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim) }} + ports: + {{- . | nindent 4 }} + {{- end -}} + {{- with (include "ix.v1.common.lib.container.volumeMount" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim) }} + volumeMounts: + {{- . | nindent 4 }} + {{- end -}} + {{- include "ix.v1.common.lib.container.probes" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 2 -}} + {{- with (include "ix.v1.common.lib.container.resources" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim) }} + resources: + {{- . | nindent 4 }} + {{- end }} + securityContext: + {{- include "ix.v1.common.lib.container.securityContext" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 4 }} + {{- /* Create a dict for storing env's so it can be checked for dupes */ -}} + {{- $_ := set $objectData "envDupe" dict -}} + {{- with (include "ix.v1.common.lib.container.envFrom" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim) }} + envFrom: + {{- . | nindent 4 }} + {{- end }} + env: + {{- include "ix.v1.common.lib.container.fixedEnv" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 4 -}} + {{- include "ix.v1.common.lib.container.env" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 4 -}} + {{- include "ix.v1.common.lib.container.envList" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 4 -}} + {{- $_ := unset $objectData "envDupe" -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/pod/_containerSpawner.tpl b/library/common/1.0.0/templates/lib/pod/_containerSpawner.tpl new file mode 100644 index 0000000000..9f7627219b --- /dev/null +++ b/library/common/1.0.0/templates/lib/pod/_containerSpawner.tpl @@ -0,0 +1,31 @@ +{{/* Containers */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.pod.containerSpawner" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the Pod. +*/}} +{{- define "ix.v1.common.lib.pod.containerSpawner" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- include "ix.v1.common.lib.container.primaryValidation" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} + + {{- range $containerName, $containerValues := $objectData.podSpec.containers -}} + {{- if $containerValues.enabled -}} + {{- $container := (mustDeepCopy $containerValues) -}} + {{- $name := include "ix.v1.common.lib.chart.names.fullname" $rootCtx -}} + {{- if not $container.primary -}} + {{- $name = printf "%s-%s" $name $containerName -}} + {{- end -}} + + {{- $_ := set $container "name" $name -}} + {{- $_ := set $container "shortName" $containerName -}} + {{- $_ := set $container "podShortName" $objectData.shortName -}} + {{- $_ := set $container "podPrimary" $objectData.primary -}} + {{- $_ := set $container "podType" $objectData.type -}} + {{/* Created from the pod.securityContext, used by fixedEnv */}} + {{- $_ := set $container "calculatedFSGroup" $objectData.podSpec.calculatedFSGroup -}} + {{- include "ix.v1.common.lib.pod.container" (dict "rootCtx" $rootCtx "objectData" $container) | trim | nindent 0 -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/pod/_dns.tpl b/library/common/1.0.0/templates/lib/pod/_dns.tpl new file mode 100644 index 0000000000..44a881cbc1 --- /dev/null +++ b/library/common/1.0.0/templates/lib/pod/_dns.tpl @@ -0,0 +1,90 @@ +{{/* Returns DNS Policy and Config */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.pod.dns" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the Pod. +*/}} +{{- define "ix.v1.common.lib.pod.dns" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $policy := "ClusterFirst" -}} + {{- $config := dict -}} + + {{/* Initialize from the "global" option */}} + {{- with $rootCtx.Values.podOptions.dnsPolicy -}} + {{- $policy = . -}} + {{- end -}} + + {{- with $rootCtx.Values.podOptions.dnsConfig -}} + {{- $config = . -}} + {{- end -}} + + {{/* Override with pod's option */}} + {{- with $objectData.podSpec.dnsPolicy -}} + {{- $policy = . -}} + {{- end -}} + + {{- with $objectData.podSpec.dnsConfig -}} + {{- $config = . -}} + {{- end -}} + + {{/* Expand policy */}} + {{- $policy = (tpl $policy $rootCtx) -}} + + {{/* If hostNetwork is enabled, then use ClusterFirstWithHostNet */}} + {{- $hostNet := include "ix.v1.common.lib.pod.hostNetwork" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} + {{- if or (and (kindIs "string" $hostNet) (eq $hostNet "true")) (and (kindIs "bool" $hostNet) $hostNet) -}} + {{- $policy = "ClusterFirstWithHostNet" -}} + {{- end -}} + + {{- $policies := (list "ClusterFirst" "ClusterFirstWithHostNet" "Default" "None") -}} + {{- if not (mustHas $policy $policies) -}} + {{- fail (printf "Expected to be one of [%s], but got [%s]" (join ", " $policies) $policy) -}} + {{- end -}} + + {{/* When policy is set to None all keys are required */}} + {{- if eq $policy "None" -}} + + {{- range $key := (list "nameservers" "searches" "options") -}} + {{- if not (get $config $key) -}} + {{- fail (printf "Expected non-empty with set to [None]." $key) -}} + {{- end -}} + {{- end -}} + + {{- end }} +dnsPolicy: {{ $policy }} + {{- if or $config.nameservers $config.options $config.searches }} +dnsConfig: + {{- with $config.nameservers -}} + {{- if gt (len .) 3 -}} + {{- fail (printf "Expected no more than [3] , but got [%v]" (len .)) -}} + {{- end }} + nameservers: + {{- range . }} + - {{ tpl . $rootCtx }} + {{- end -}} + {{- end -}} + + {{- with $config.searches -}} + {{- if gt (len .) 6 -}} + {{- fail (printf "Expected no more than [6] , but got [%v]" (len .)) -}} + {{- end }} + searches: + {{- range . }} + - {{ tpl . $rootCtx }} + {{- end -}} + {{- end -}} + + {{- with $config.options }} + options: + {{- range . }} + - name: {{ tpl .name $rootCtx }} + {{- with .value }} + value: {{ tpl . $rootCtx | quote }} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/pod/_enableServiceLinks.tpl b/library/common/1.0.0/templates/lib/pod/_enableServiceLinks.tpl new file mode 100644 index 0000000000..a5e5dedfb0 --- /dev/null +++ b/library/common/1.0.0/templates/lib/pod/_enableServiceLinks.tpl @@ -0,0 +1,24 @@ +{{/* Returns enableServiceLinks */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.pod.enableServiceLinks" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the Pod. +*/}} +{{- define "ix.v1.common.lib.pod.enableServiceLinks" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $enableServiceLinks := false -}} + + {{/* Initialize from the "global" option */}} + {{- if (kindIs "bool" $rootCtx.Values.podOptions.enableServiceLinks) -}} + {{- $enableServiceLinks = $rootCtx.Values.podOptions.enableServiceLinks -}} + {{- end -}} + + {{/* Override with pod's option */}} + {{- if (kindIs "bool" $objectData.podSpec.enableServiceLinks) -}} + {{- $enableServiceLinks = $objectData.podSpec.enableServiceLinks -}} + {{- end -}} + + {{- $enableServiceLinks -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/pod/_hostAliases.tpl b/library/common/1.0.0/templates/lib/pod/_hostAliases.tpl new file mode 100644 index 0000000000..4eb47a293a --- /dev/null +++ b/library/common/1.0.0/templates/lib/pod/_hostAliases.tpl @@ -0,0 +1,37 @@ +{{/* Returns Host Aliases */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.pod.hostAliases" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the Pod. +*/}} +{{- define "ix.v1.common.lib.pod.hostAliases" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $aliases := list -}} + + {{/* Initialize from the "global" option */}} + {{- with $rootCtx.Values.podOptions.hostAliases -}} + {{- $aliases = . -}} + {{- end -}} + + {{/* Override with pod's option */}} + {{- with $objectData.podSpec.hostAliases -}} + {{- $aliases = . -}} + {{- end -}} + + {{- range $aliases -}} + {{- if not .ip -}} + {{- fail (printf "Expected non-empty value on .") -}} + {{- end -}} + + {{- if not .hostnames -}} + {{- fail (printf "Expected non-empty list on .") -}} + {{- end }} +- ip: {{ tpl .ip $rootCtx }} + hostnames: + {{- range .hostnames }} + - {{ tpl . $rootCtx }} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/pod/_hostNetwork.tpl b/library/common/1.0.0/templates/lib/pod/_hostNetwork.tpl new file mode 100644 index 0000000000..e02d034923 --- /dev/null +++ b/library/common/1.0.0/templates/lib/pod/_hostNetwork.tpl @@ -0,0 +1,24 @@ +{{/* Returns Host Network */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.pod.hostNetwork" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the Pod. +*/}} +{{- define "ix.v1.common.lib.pod.hostNetwork" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $hostNet := false -}} + + {{/* Initialize from the "global" option */}} + {{- if (kindIs "bool" $rootCtx.Values.podOptions.hostNetwork) -}} + {{- $hostNet = $rootCtx.Values.podOptions.hostNetwork -}} + {{- end -}} + + {{/* Override with pod's option */}} + {{- if (kindIs "bool" $objectData.podSpec.hostNetwork) -}} + {{- $hostNet = $objectData.podSpec.hostNetwork -}} + {{- end -}} + + {{- $hostNet -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/pod/_hostname.tpl.tpl b/library/common/1.0.0/templates/lib/pod/_hostname.tpl.tpl new file mode 100644 index 0000000000..f16bad7e8b --- /dev/null +++ b/library/common/1.0.0/templates/lib/pod/_hostname.tpl.tpl @@ -0,0 +1,22 @@ +{{/* Returns Host Name */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.pod.hostname" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the Pod. +*/}} +{{- define "ix.v1.common.lib.pod.hostname" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $hostname := "" -}} + + {{- with $objectData.podSpec.hostname -}} + {{- $hostname = tpl . $rootCtx -}} + {{- end -}} + + {{- if $hostname -}} + {{- include "ix.v1.common.lib.chart.names.validation" (dict "name" $hostname) -}} + {{- end -}} + + {{- $hostname -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/pod/_imagePullSecret.tpl b/library/common/1.0.0/templates/lib/pod/_imagePullSecret.tpl new file mode 100644 index 0000000000..d8ee4833fc --- /dev/null +++ b/library/common/1.0.0/templates/lib/pod/_imagePullSecret.tpl @@ -0,0 +1,38 @@ +{{/* Returns Image Pull Secret List */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.pod.imagePullSecret" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the Pod. +*/}} +{{- define "ix.v1.common.lib.pod.imagePullSecret" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $imgPullSecrets := list -}} + + {{- range $name, $imgPull := $rootCtx.Values.imagePullSecret -}} + {{- $pullName := (printf "%s-%s" (include "ix.v1.common.lib.chart.names.fullname" $rootCtx) $name) -}} + + {{- if $imgPull.enabled -}} + {{/* If targetSelectAll is true */}} + {{- if $imgPull.targetSelectAll -}} + {{- $imgPullSecrets = mustAppend $imgPullSecrets $pullName -}} + + {{/* Else if targetSelector is a list */}} + {{- else if (kindIs "slice" $imgPull.targetSelector) -}} + {{- if (mustHas $objectData.shortName $imgPull.targetSelector) -}} + {{- $imgPullSecrets = mustAppend $imgPullSecrets $pullName -}} + {{- end -}} + + {{/* If not targetSelectAll or targetSelector, but is the primary pod */}} + {{- else if $objectData.primary -}} + {{- $imgPullSecrets = mustAppend $imgPullSecrets $pullName -}} + {{- end -}} + + {{- end -}} + {{- end -}} + + {{- range $imgPullSecrets }} +- name: {{ . }} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/pod/_initContainerSpawner.tpl b/library/common/1.0.0/templates/lib/pod/_initContainerSpawner.tpl new file mode 100644 index 0000000000..52f88a059b --- /dev/null +++ b/library/common/1.0.0/templates/lib/pod/_initContainerSpawner.tpl @@ -0,0 +1,83 @@ +{{/* Init Containers */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.pod.initContainerSpawner" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the Pod. +*/}} +{{- define "ix.v1.common.lib.pod.initContainerSpawner" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $initContainers := (dict "init" list + "install" list + "upgrade" list) -}} + + {{- $types := (list "init" "install" "upgrade") -}} + {{- range $containerName, $containerValues := $objectData.podSpec.initContainers -}} + + {{- $enabled := $containerValues.enabled -}} + {{- if kindIs "string" $enabled -}} + {{- $enabled = tpl $enabled $rootCtx -}} + + {{/* After tpl it becomes a string, not a bool */}} + {{- if eq $enabled "true" -}} + {{- $enabled = true -}} + {{- else -}} + {{- $enabled = false -}} + {{- end -}} + {{- end -}} + + {{- if $enabled -}} + + {{- if not ($containerValues.type) -}} + {{- fail "InitContainer - Expected non-empty " -}} + {{- end -}} + + {{- $containerType := tpl $containerValues.type $rootCtx -}} + {{- if not (mustHas $containerType $types) -}} + {{- fail (printf "InitContainer - Expected to be one of [%s], but got [%s]" (join ", " $types) $containerType) -}} + {{- end -}} + + {{- $container := (mustDeepCopy $containerValues) -}} + {{- $name := printf "%s-%s-%s" (include "ix.v1.common.lib.chart.names.fullname" $rootCtx) $containerType $containerName -}} + + {{- $_ := set $container "name" $name -}} + {{- $_ := set $container "shortName" $containerName -}} + {{- $_ := set $container "podShortName" $objectData.shortName -}} + {{- $_ := set $container "podPrimary" $objectData.primary -}} + {{- $_ := set $container "podType" $objectData.type -}} + + {{/* Remove keys that do not apply on init containers */}} + {{- $_ := set $container "lifecycle" dict -}} + {{- $_ := set $container "probes" dict -}} + {{/* Template expects probes dict defined even if enabled */}} + {{- $_ := set $container.probes "liveness" (dict "enabled" false) -}} + {{- $_ := set $container.probes "readiness" (dict "enabled" false) -}} + {{- $_ := set $container.probes "startup" (dict "enabled" false) -}} + + {{/* Created from the pod.securityContext, used by fixedEnv */}} + {{- $_ := set $container "calculatedFSGroup" $objectData.podSpec.calculatedFSGroup -}} + + {{/* Append to list of containers based on type */}} + {{- $tempContainers := (get $initContainers $containerType) -}} + {{- $_ := set $initContainers $containerType (mustAppend $tempContainers $container) -}} + {{- end -}} + {{- end -}} + + {{- if $rootCtx.Release.IsInstall -}} + {{- range $container := (get $initContainers "install") -}} + {{- include "ix.v1.common.lib.pod.container" (dict "rootCtx" $rootCtx "objectData" $container) -}} + {{- end -}} + {{- end -}} + + {{- if $rootCtx.Release.IsUpgrade -}} + {{- range $container := (get $initContainers "upgrade") -}} + {{- include "ix.v1.common.lib.pod.container" (dict "rootCtx" $rootCtx "objectData" $container) -}} + {{- end -}} + {{- end -}} + + {{- range $container := (get $initContainers "init") -}} + {{- include "ix.v1.common.lib.pod.container" (dict "rootCtx" $rootCtx "objectData" $container) -}} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/pod/_podSecurityContext.tpl b/library/common/1.0.0/templates/lib/pod/_podSecurityContext.tpl new file mode 100644 index 0000000000..5a7225b864 --- /dev/null +++ b/library/common/1.0.0/templates/lib/pod/_podSecurityContext.tpl @@ -0,0 +1,90 @@ +{{/* Returns Pod Security Context */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.pod.securityContext" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the Pod. +*/}} +{{- define "ix.v1.common.lib.pod.securityContext" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- if not $rootCtx.Values.securityContext.pod -}} + {{- fail "Pod - Expected non-empty <.Values.securityContext.pod>" -}} + {{- end -}} + + {{/* Initialize from the "global" option */}} + {{- $secContext := mustDeepCopy $rootCtx.Values.securityContext.pod -}} + + {{/* Override with pod's option */}} + {{- with $objectData.podSpec.securityContext -}} + {{- $secContext = mustMergeOverwrite $secContext . -}} + {{- end -}} + + {{/* TODO: Add supplemental groups + devices (5, 10, 20, 24) (Only when devices is assigned on the pod's containers) + TODO: Unit Test the above cases + */}} + + {{- $addSupplemental := list -}} + {{- range $GPUValues := $rootCtx.Values.scaleGPU -}} + {{/* If there is a selector and pod is selected */}} + {{- if $GPUValues.targetSelector -}} + {{- if mustHas $objectData.shortName ($GPUValues.targetSelector | keys) -}} + {{- $addSupplemental = mustAppend $addSupplemental 44 -}} + {{- end -}} + {{/* If there isn't a selector, but pod is primary */}} + {{- else if $objectData.primary -}} + {{- $addSupplemental = mustAppend $addSupplemental 44 -}} + {{- end -}} + {{- end -}} + + {{- if $addSupplemental -}} + {{- $_ := set $secContext "supplementalGroups" (concat $secContext.supplementalGroups $addSupplemental) -}} + {{- end -}} + + {{- $portRange := fromJson (include "ix.v1.common.lib.helpers.securityContext.getPortRange" (dict "rootCtx" $rootCtx "objectData" $objectData)) -}} + {{- if and $portRange.low (le (int $portRange.low) 1024) -}} {{/* If a container wants to bind a port <= 1024 change the unprivileged_port_start */}} + {{- $_ := set $secContext "sysctls" (mustAppend $secContext.sysctls (dict "name" "net.ipv4.ip_unprivileged_port_start" "value" (printf "%v" $portRange.low))) -}} + {{- end -}} + + {{- if not $secContext.fsGroup -}} + {{- fail "Pod - Expected non-empty " -}} + {{- end -}} + + {{/* Used by the fixedEnv template */}} + {{- $_ := set $objectData.podSpec "calculatedFSGroup" $secContext.fsGroup -}} + + {{- if not $secContext.fsGroupChangePolicy -}} + {{- fail "Pod - Expected non-empty " -}} + {{- end -}} + + {{- $policies := (list "Always" "OnRootMismatch") -}} + {{- if not (mustHas $secContext.fsGroupChangePolicy $policies) -}} + {{- fail (printf "Pod - Expected to be one of [%s], but got [%s]" (join ", " $policies) $secContext.fsGroupChangePolicy) -}} + {{- end }} +fsGroup: {{ $secContext.fsGroup }} +fsGroupChangePolicy: {{ $secContext.fsGroupChangePolicy }} + {{- with $secContext.supplementalGroups }} +supplementalGroups: + {{- range . }} + - {{ . }} + {{- end -}} + {{- else }} +supplementalGroups: [] + {{- end -}} + {{- with $secContext.sysctls }} +sysctls: + {{- range . }} + {{- if not .name -}} + {{- fail "Pod - Expected non-empty in " -}} + {{- end -}} + {{- if not .value -}} + {{- fail "Pod - Expected non-empty in " -}} + {{- end }} + - name: {{ tpl .name $rootCtx | quote }} + value: {{ tpl .value $rootCtx | quote }} + {{- end -}} + {{- else }} +sysctls: [] + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/pod/_restartPolicy.tpl b/library/common/1.0.0/templates/lib/pod/_restartPolicy.tpl new file mode 100644 index 0000000000..2c1277e59e --- /dev/null +++ b/library/common/1.0.0/templates/lib/pod/_restartPolicy.tpl @@ -0,0 +1,34 @@ +{{/* Returns Restart Policy */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.pod.restartPolicy" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the Pod. +*/}} +{{- define "ix.v1.common.lib.pod.restartPolicy" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $policy := "Always" -}} + + {{/* Initialize from the "defaults" */}} + {{- with $rootCtx.Values.podOptions.restartPolicy -}} + {{- $policy = tpl . $rootCtx -}} + {{- end -}} + + {{/* Override from the pod values, if defined */}} + {{- with $objectData.podSpec.restartPolicy -}} + {{- $policy = tpl . $rootCtx -}} + {{- end -}} + + {{- $policies := (list "Never" "Always" "OnFailure") -}} + {{- if not (mustHas $policy $policies) -}} + {{- fail (printf "Expected to be one of [%s] but got [%s]" (join ", " $operators) $operator) -}} + {{- end -}} + + {{- if and (eq $operator "Equal") (or (not $key) (not $value)) -}} + {{- fail "Expected non-empty and with set to [Equal]" -}} + {{- end -}} + + {{- if and (eq $operator "Exists") $value -}} + {{- fail (printf "Expected empty with set to [Exists], but got [%s]" $value) -}} + {{- end -}} + + {{- $effects := (list "NoExecute" "NoSchedule" "PreferNoSchedule") -}} + {{- if and $effect (not (mustHas $effect $effects)) -}} + {{- fail (printf "Expected to be one of [%s], but got [%s]" (join ", " $effects) $effect) -}} + {{- end -}} + + {{- if and (not (kindIs "invalid" $tolSeconds)) (not (mustHas (kindOf $tolSeconds) (list "int" "float64"))) -}} + {{- fail (printf "Expected to be a number, but got [%s]" $tolSeconds) -}} + {{- end }} +- operator: {{ $operator }} + {{- with $key }} + key: {{ $key }} + {{- end -}} + {{- with $effect }} + effect: {{ $effect }} + {{- end -}} + {{- with $value }} + value: {{ . }} + {{- end -}} + {{- if (mustHas (kindOf $tolSeconds) (list "int" "float64")) }} + tolerationSeconds: {{ $tolSeconds }} + {{- end -}} + + {{- end -}} +{{- end -}} 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..a96ace95b8 --- /dev/null +++ b/library/common/1.0.0/templates/lib/pod/_volumes.tpl @@ -0,0 +1,62 @@ +{{/* Returns Volumes */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.pod.volumes" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +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 -}} + {{- $_ := set $persistence "type" ($persistence.type | default $rootCtx.Values.fallbackDefaults.persistenceType) -}} + {{- include "ix.v1.common.lib.persistence.validation" (dict "rootCtx" $rootCtx "objectData" $persistence) -}} + + {{- $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 "ixVolume" $type -}} + {{- include "ix.v1.common.lib.pod.volume.ixVolume" (dict "rootCtx" $rootCtx "objectData" $persistence) | trim | nindent 0 -}} + {{- else if eq "hostPath" $type -}} + {{- include "ix.v1.common.lib.pod.volume.hostPath" (dict "rootCtx" $rootCtx "objectData" $persistence) | trim | nindent 0 -}} + {{- else if eq "secret" $type -}} + {{- include "ix.v1.common.lib.pod.volume.secret" (dict "rootCtx" $rootCtx "objectData" $persistence) | trim | nindent 0 -}} + {{- else if eq "configmap" $type -}} + {{- include "ix.v1.common.lib.pod.volume.configmap" (dict "rootCtx" $rootCtx "objectData" $persistence) | trim | nindent 0 -}} + {{- else if eq "emptyDir" $type -}} + {{- include "ix.v1.common.lib.pod.volume.emptyDir" (dict "rootCtx" $rootCtx "objectData" $persistence) | trim | nindent 0 -}} + {{- else if eq "device" $type -}} + {{- include "ix.v1.common.lib.pod.volume.device" (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/_configmap.tpl b/library/common/1.0.0/templates/lib/pod/volumes/_configmap.tpl new file mode 100644 index 0000000000..7ca0b61b1d --- /dev/null +++ b/library/common/1.0.0/templates/lib/pod/volumes/_configmap.tpl @@ -0,0 +1,57 @@ +{{/* Returns ConfigMap Volume */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.pod.volume.configmap" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the volume. +*/}} +{{- define "ix.v1.common.lib.pod.volume.configmap" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- if not $objectData.objectName -}} + {{- fail "Persistence - Expected non-empty on type" -}} + {{- end -}} + + {{- $objectName := tpl $objectData.objectName $rootCtx -}} + {{- $expandName := true -}} + {{- if kindIs "bool" $objectData.expandObjectName -}} + {{- $expandName = $objectData.expandObjectName -}} + {{- end -}} + + {{- if $expandName -}} + {{- $objectName = (printf "%s-%s" (include "ix.v1.common.lib.chart.names.fullname" $rootCtx) $objectName) -}} + {{- end -}} + + {{- $defMode := "" -}} + + {{- if (and $objectData.defaultMode (not (kindIs "string" $objectData.defaultMode))) -}} + {{- fail (printf "Persistence - Expected to be [string], but got [%s]" (kindOf $objectData.defaultMode)) -}} + {{- end -}} + + {{- with $objectData.defaultMode -}} + {{- $defMode = tpl $objectData.defaultMode $rootCtx -}} + {{- end -}} + + {{- if and $defMode (not (mustRegexMatch "^[0-9]{4}$" $defMode)) -}} + {{- fail (printf "Persistence - Expected to have be in format of [\"0777\"], but got [%q]" $defMode) -}} + {{- end }} +- name: {{ $objectData.shortName }} + configMap: + name: {{ $objectName }} + {{- with $defMode }} + defaultMode: {{ . }} + {{- end -}} + {{- with $objectData.items }} + items: + {{- range . -}} + {{- if not .key -}} + {{- fail "Persistence - Expected non-empty " -}} + {{- end -}} + {{- if not .path -}} + {{- fail "Persistence - Expected non-empty " -}} + {{- end }} + - key: {{ tpl .key $rootCtx }} + path: {{ tpl .path $rootCtx }} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/pod/volumes/_device.tpl b/library/common/1.0.0/templates/lib/pod/volumes/_device.tpl new file mode 100644 index 0000000000..a0dc0a4aa2 --- /dev/null +++ b/library/common/1.0.0/templates/lib/pod/volumes/_device.tpl @@ -0,0 +1,35 @@ +{{/* Returns device (hostPath) Volume */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.pod.volume.device" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the volume. +*/}} +{{- define "ix.v1.common.lib.pod.volume.device" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $hostPathType := "" -}} + {{- if $objectData.hostPathType -}} + {{- $hostPathType = tpl $objectData.hostPathType $rootCtx -}} + {{- end -}} + + {{- if not $objectData.hostPath -}} + {{- fail "Persistence - Expected non-empty on type" -}} + {{- end -}} + {{- $hostPath := tpl $objectData.hostPath $rootCtx -}} + + {{- if not (hasPrefix "/" $hostPath) -}} + {{- fail "Persistence - Expected to start with a forward slash [/] on type" -}} + {{- end -}} + + {{- $types := (list "DirectoryOrCreate" "Directory" "FileOrCreate" "File" "Socket" "CharDevice" "BlockDevice") -}} + {{- if and $hostPathType (not (mustHas $hostPathType $types)) -}} + {{- fail (printf "Persistence - Expected to be one of [%s], but got [%s]" (join ", " $types) $hostPathType) -}} + {{- end }} +- name: {{ $objectData.shortName }} + hostPath: + path: {{ $hostPath }} + {{- with $hostPathType }} + type: {{ $hostPathType }} + {{- 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..0b85f4ef86 --- /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 chart. +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 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/_hostPath.tpl b/library/common/1.0.0/templates/lib/pod/volumes/_hostPath.tpl new file mode 100644 index 0000000000..cf62bb6a7c --- /dev/null +++ b/library/common/1.0.0/templates/lib/pod/volumes/_hostPath.tpl @@ -0,0 +1,35 @@ +{{/* Returns hostPath Volume */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.pod.volume.hostPath" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the volume. +*/}} +{{- define "ix.v1.common.lib.pod.volume.hostPath" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $hostPathType := "" -}} + {{- if $objectData.hostPathType -}} + {{- $hostPathType = tpl $objectData.hostPathType $rootCtx -}} + {{- end -}} + + {{- if not $objectData.hostPath -}} + {{- fail "Persistence - Expected non-empty on type" -}} + {{- end -}} + {{- $hostPath := tpl $objectData.hostPath $rootCtx -}} + + {{- if not (hasPrefix "/" $hostPath) -}} + {{- fail "Persistence - Expected to start with a forward slash [/] on type" -}} + {{- end -}} + + {{- $types := (list "DirectoryOrCreate" "Directory" "FileOrCreate" "File" "Socket" "CharDevice" "BlockDevice") -}} + {{- if and $hostPathType (not (mustHas $hostPathType $types)) -}} + {{- fail (printf "Persistence - Expected to be one of [%s], but got [%s]" (join ", " $types) $hostPathType) -}} + {{- end }} +- name: {{ $objectData.shortName }} + hostPath: + path: {{ $hostPath }} + {{- with $hostPathType }} + type: {{ $hostPathType }} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/pod/volumes/_ixVolume.tpl b/library/common/1.0.0/templates/lib/pod/volumes/_ixVolume.tpl new file mode 100644 index 0000000000..61b12c3c43 --- /dev/null +++ b/library/common/1.0.0/templates/lib/pod/volumes/_ixVolume.tpl @@ -0,0 +1,48 @@ +{{/* Returns ixVolume Volume */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.pod.volume.ixVolume" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the volume. +*/}} +{{- define "ix.v1.common.lib.pod.volume.ixVolume" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $hostPathType := "" -}} + {{- if $objectData.hostPathType -}} + {{- $hostPathType = tpl $objectData.hostPathType $rootCtx -}} + {{- end -}} + + {{- if not $objectData.datasetName -}} + {{- fail "Persistence - Expected non-empty on type" -}} + {{- end -}} + {{- $datasetName := tpl $objectData.datasetName $rootCtx -}} + + {{- if not $rootCtx.Values.ixVolumes -}} + {{- fail "Persistence - Expected non-empty in values on type" -}} + {{- end -}} + + {{- $hostPath := "" -}} + {{- range $idx, $normalizedHostPath := $rootCtx.Values.ixVolumes -}} + {{- if eq $datasetName (base $normalizedHostPath) -}} + {{- $hostPath = $normalizedHostPath -}} + {{- else -}} + {{- fail (printf "Persistence - Expected [%s] to exist on list, but list contained [%s] on type" $datasetName (join ", " $rootCtx.Values.ixVolumes )) -}} + {{- end -}} + {{- end -}} + + {{- if not (hasPrefix "/" $hostPath) -}} + {{- fail "Persistence - Expected normalized path from to start with a forward slash [/] on type" -}} + {{- end -}} + + {{- $types := (list "DirectoryOrCreate" "Directory" "FileOrCreate" "File" "Socket" "CharDevice" "BlockDevice") -}} + {{- if and $hostPathType (not (mustHas $hostPathType $types)) -}} + {{- fail (printf "Persistence - Expected to be one of [%s], but got [%s]" (join ", " $types) $hostPathType) -}} + {{- end }} +- name: {{ $objectData.shortName }} + hostPath: + path: {{ $hostPath }} + {{- with $hostPathType }} + type: {{ $hostPathType }} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/pod/volumes/_secret.tpl b/library/common/1.0.0/templates/lib/pod/volumes/_secret.tpl new file mode 100644 index 0000000000..c104235307 --- /dev/null +++ b/library/common/1.0.0/templates/lib/pod/volumes/_secret.tpl @@ -0,0 +1,57 @@ +{{/* Returns Secret Volume */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.pod.volume.secret" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the volume. +*/}} +{{- define "ix.v1.common.lib.pod.volume.secret" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- if not $objectData.objectName -}} + {{- fail "Persistence - Expected non-empty on type" -}} + {{- end -}} + + {{- $objectName := tpl $objectData.objectName $rootCtx -}} + {{- $expandName := true -}} + {{- if kindIs "bool" $objectData.expandObjectName -}} + {{- $expandName = $objectData.expandObjectName -}} + {{- end -}} + + {{- if $expandName -}} + {{- $objectName = (printf "%s-%s" (include "ix.v1.common.lib.chart.names.fullname" $rootCtx) $objectName) -}} + {{- end -}} + + {{- $defMode := "" -}} + + {{- if (and $objectData.defaultMode (not (kindIs "string" $objectData.defaultMode))) -}} + {{- fail (printf "Persistence - Expected to be [string], but got [%s]" (kindOf $objectData.defaultMode)) -}} + {{- end -}} + + {{- with $objectData.defaultMode -}} + {{- $defMode = tpl $objectData.defaultMode $rootCtx -}} + {{- end -}} + + {{- if and $defMode (not (mustRegexMatch "^[0-9]{4}$" $defMode)) -}} + {{- fail (printf "Persistence - Expected to have be in format of [\"0777\"], but got [%q]" $defMode) -}} + {{- end }} +- name: {{ $objectData.shortName }} + secret: + secretName: {{ $objectName }} + {{- with $defMode }} + defaultMode: {{ . }} + {{- end -}} + {{- with $objectData.items }} + items: + {{- range . -}} + {{- if not .key -}} + {{- fail "Persistence - Expected non-empty " -}} + {{- end -}} + {{- if not .path -}} + {{- fail "Persistence - Expected non-empty " -}} + {{- end }} + - key: {{ tpl .key $rootCtx }} + path: {{ tpl .path $rootCtx }} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/rbac/_getServiceAccounts.tpl b/library/common/1.0.0/templates/lib/rbac/_getServiceAccounts.tpl new file mode 100644 index 0000000000..3d4149ac13 --- /dev/null +++ b/library/common/1.0.0/templates/lib/rbac/_getServiceAccounts.tpl @@ -0,0 +1,52 @@ +{{/* Returns Service Account List for rbac */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.rbac.serviceAccount" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the RBAC. +*/}} +{{/* Parses service accounts, and checks if RBAC have selected any of them */}} +{{- define "ix.v1.common.lib.rbac.serviceAccount" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $serviceAccounts := list -}} + + {{- range $name, $serviceAccount := $rootCtx.Values.serviceAccount -}} + {{- $saName := include "ix.v1.common.lib.chart.names.fullname" $rootCtx -}} + + {{- if $serviceAccount.enabled -}} + + {{- if not $serviceAccount.primary -}} + {{- $saName = (printf "%s-%s" (include "ix.v1.common.lib.chart.names.fullname" $rootCtx) $name) -}} + {{- end -}} + + {{/* If allServiceAccounts is true */}} + {{- if $objectData.allServiceAccounts -}} + {{- $serviceAccounts = mustAppend $serviceAccounts $saName -}} + + {{/* Else if serviceAccounts is a list */}} + {{- else if (kindIs "slice" $objectData.serviceAccounts) -}} + {{- if (mustHas $name $objectData.serviceAccounts) -}} + {{- $serviceAccounts = mustAppend $serviceAccounts $saName -}} + {{- end -}} + + {{/* If not "allServiceAccounts" or "serviceAccounts", assign the primary service account to rbac */}} + {{- else if $serviceAccount.primary -}} + {{- if $objectData.primary -}} + {{- $serviceAccounts = mustAppend $serviceAccounts $saName -}} + {{- end -}} + {{- end -}} + + {{- end -}} + {{- end -}} + + {{- if not $serviceAccounts -}} + {{- fail "RBAC - Expected at least one serviceAccount to be assigned. Assign one using [allServiceAccounts (boolean), serviceAccounts (list)]" -}} + {{- end -}} + + {{- range $serviceAccounts }} +- kind: ServiceAccount + name: {{ . }} + namespace: {{ $rootCtx.Release.Namespace }} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/rbac/_rules.tpl b/library/common/1.0.0/templates/lib/rbac/_rules.tpl new file mode 100644 index 0000000000..e166fa16b8 --- /dev/null +++ b/library/common/1.0.0/templates/lib/rbac/_rules.tpl @@ -0,0 +1,50 @@ +{{/* Returns Rules for rbac */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.rbac.rules" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the RBAC. +*/}} +{{/* Parses service accounts, and checks if RBAC have selected any of them */}} +{{- define "ix.v1.common.lib.rbac.rules" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- if not $objectData.rules -}} + {{- fail "RBAC - Expected non-empty " -}} + {{- end -}} + + {{- range $objectData.rules -}} + {{- if not .apiGroups -}} + {{- fail "RBAC - Expected non-empty " -}} + {{- end -}} + {{- if not .resources -}} + {{- fail "RBAC - Expected non-empty " -}} + {{- end -}} + {{- if not .verbs -}} + {{- fail "RBAC - Expected non-empty " -}} + {{- end -}} + + {{- /* apiGroups */}} +- apiGroups: + {{- range .apiGroups }} + - {{ tpl . $rootCtx | quote }} + {{- end -}} + {{- /* resources */}} + resources: + {{- range .resources -}} + {{- if not . -}} + {{- fail "RBAC - Expected non-empty entry in " -}} + {{- end }} + - {{ tpl . $rootCtx | quote }} + {{- end -}} + {{- /* verbs */}} + verbs: + {{- range .verbs -}} + {{- if not . -}} + {{- fail "RBAC - Expected non-empty entry in " -}} + {{- end }} + - {{ tpl . $rootCtx | quote }} + {{- end -}} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/rbac/_subjects.tpl b/library/common/1.0.0/templates/lib/rbac/_subjects.tpl new file mode 100644 index 0000000000..6559fcdbec --- /dev/null +++ b/library/common/1.0.0/templates/lib/rbac/_subjects.tpl @@ -0,0 +1,17 @@ +{{/* Returns Subjects for rbac */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.rbac.subjects" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the RBAC. +*/}} +{{/* Parses service accounts, and checks if RBAC have selected any of them */}} +{{- define "ix.v1.common.lib.rbac.subjects" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- range $objectData.subjects }} +- kind: {{ tpl (required "RBAC - Expected non-empty " .kind) $rootCtx | quote }} + name: {{ tpl (required "RBAC - Expected non-empty " .name) $rootCtx | quote }} + apiGroup: {{ tpl (required "RBAC - Expected non-empty " .apiGroup) $rootCtx | quote }} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/rbac/_validation.tpl b/library/common/1.0.0/templates/lib/rbac/_validation.tpl new file mode 100644 index 0000000000..7aa7fffe25 --- /dev/null +++ b/library/common/1.0.0/templates/lib/rbac/_validation.tpl @@ -0,0 +1,38 @@ +{{/* RBAC Primary Validation */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.rbac.primaryValidation" $ -}} +*/}} + +{{- define "ix.v1.common.lib.rbac.primaryValidation" -}} + + {{/* Initialize values */}} + {{- $hasPrimary := false -}} + {{- $hasEnabled := false -}} + + {{- range $name, $rbac := .Values.rbac -}} + + {{/* If rbac is enabled */}} + {{- if $rbac.enabled -}} + {{- $hasEnabled = true -}} + + {{/* And rbac is primary */}} + {{- if and (hasKey $rbac "primary") ($rbac.primary) -}} + + {{/* Fail if there is already a primary rbac */}} + {{- if $hasPrimary -}} + {{- fail "RBAC - Only one rbac can be primary" -}} + {{- end -}} + + {{- $hasPrimary = true -}} + + {{- end -}} + + {{- end -}} + {{- end -}} + + {{/* Require at least one primary rbac, if any enabled */}} + {{- if and $hasEnabled (not $hasPrimary) -}} + {{- fail "RBAC - At least one enabled rbac must be primary" -}} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/secret/_validation.tpl b/library/common/1.0.0/templates/lib/secret/_validation.tpl new file mode 100644 index 0000000000..410b612f3b --- /dev/null +++ b/library/common/1.0.0/templates/lib/secret/_validation.tpl @@ -0,0 +1,25 @@ +{{/* Secret Validation */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.secret.validation" (dict "objectData" $objectData) -}} +objectData: + labels: The labels of the secret. + annotations: The annotations of the secret. + data: The data of the secret. +*/}} + +{{- define "ix.v1.common.lib.secret.validation" -}} + {{- $objectData := .objectData -}} + + {{- if not $objectData.data -}} + {{- fail "Secret - Expected non-empty " -}} + {{- end -}} + + {{- if not (kindIs "map" $objectData.data) -}} + {{- fail (printf "Secret - Expected to be a dictionary, but got [%v]" (kindOf $objectData.data)) -}} + {{- end -}} + + {{- if and (hasKey $objectData "type") (not $objectData.type) -}} + {{- fail (printf "Secret - Found key, but it's empty") -}} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/service/_ports.tpl b/library/common/1.0.0/templates/lib/service/_ports.tpl new file mode 100644 index 0000000000..b5b60f8531 --- /dev/null +++ b/library/common/1.0.0/templates/lib/service/_ports.tpl @@ -0,0 +1,63 @@ +{{/* Service - Ports */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.service.ports" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} +rootCtx: The root context of the chart. +objectData: The object data of the service +*/}} + +{{- define "ix.v1.common.lib.service.ports" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $tcpProtocols := (list "tcp" "http" "https") -}} + {{- range $name, $portValues := $objectData.ports -}} + {{- if $portValues.enabled -}} + {{- $protocol := $rootCtx.Values.fallbackDefaults.serviceProtocol -}} {{/* Default to fallback protocol, if no protocol is defined */}} + {{- $port := $portValues.port -}} + {{- $targetPort := $portValues.targetPort -}} + {{- $nodePort := $portValues.nodePort -}} + + {{/* Expand port */}} + {{- if (kindIs "string" $port) -}} + {{- $port = (tpl $port $rootCtx) -}} + {{- end -}} + {{- $port = int $port -}} + + {{/* Expand targetPort */}} + {{- if (kindIs "string" $targetPort) -}} + {{- $targetPort = tpl $targetPort $rootCtx -}} + {{- end -}} + {{- $targetPort = int $targetPort -}} + + {{/* Expand nodePort */}} + {{- if (kindIs "string" $nodePort) -}} + {{- $nodePort = tpl $nodePort $rootCtx -}} + {{- end -}} + {{- $nodePort = int $nodePort -}} + + {{- with $portValues.protocol -}} + {{- $protocol = tpl . $rootCtx -}} + + {{- if mustHas $protocol $tcpProtocols -}} + {{- $protocol = "tcp" -}} + {{- end -}} + {{- end }} +- name: {{ $name }} + port: {{ $port }} + protocol: {{ $protocol | upper }} + targetPort: {{ $targetPort | default $port }} {{/* If no targetPort, default to port */}} + {{- if (eq $objectData.type "NodePort") -}} + {{- if not $nodePort -}} + {{- fail "Service - Expected non-empty on NodePort service type" -}} + {{- end -}} + + {{- $minNodePort := int $rootCtx.Values.global.minNodePort -}} + {{- if (lt $nodePort $minNodePort) -}} + {{- fail (printf "Service - Expected to be higher than [%v], but got [%v]" $minNodePort $nodePort) -}} + {{- end }} + nodePort: {{ $nodePort }} + {{- end -}} + {{- end -}} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/service/_validation.tpl b/library/common/1.0.0/templates/lib/service/_validation.tpl new file mode 100644 index 0000000000..b9afc90655 --- /dev/null +++ b/library/common/1.0.0/templates/lib/service/_validation.tpl @@ -0,0 +1,131 @@ +{{/* Service Validation */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.service.validation" (dict "objectData" $objectData) -}} +objectData: + rootCtx: The root context of the chart. + objectData: The service object. +*/}} + +{{- define "ix.v1.common.lib.service.validation" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- if and $objectData.targetSelector (not (kindIs "string" $objectData.targetSelector)) -}} + {{- fail (printf "Service - Expected to be [string], but got [%s]" (kindOf $objectData.targetSelector)) -}} + {{- end -}} + + {{- $svcTypes := (list "ClusterIP" "NodePort") -}} + {{- if and $objectData.type (not (mustHas $objectData.type $svcTypes)) -}} + {{- fail (printf "Service - Expected to be one of [%s] but got [%s]" (join ", " $svcTypes) $objectData.type) -}} + {{- end -}} + + {{- $hasEnabledPort := false -}} + {{- range $name, $port := $objectData.ports -}} + {{- if $port.enabled -}} + {{- $hasEnabledPort = true -}} + + {{- if and $port.targetSelector (not (kindIs "string" $port.targetSelector)) -}} + {{- fail (printf "Service - Expected to be [string], but got [%s]" (kindOf $port.targetSelector)) -}} + {{- end -}} + + {{- if not $port.port -}} + {{- fail (printf "Service - Expected non-empty ") -}} + {{- end -}} + + {{- $protocolTypes := (list "tcp" "udp" "http" "https") -}} + {{- if $port.protocol -}} + {{- if not (mustHas (tpl $port.protocol $rootCtx) $protocolTypes) -}} + {{- fail (printf "Service - Expected to be one of [%s] but got [%s]" (join ", " $protocolTypes) $port.protocol) -}} + {{- end -}} + {{- end -}} + + {{- end -}} + {{- end -}} + + {{- if not $hasEnabledPort -}} + {{- fail "Service - Expected enabled service to have at least one port" -}} + {{- end -}} + +{{- end -}} + +{{/* Service Primary Validation */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.service.primaryValidation" $ -}} +*/}} + +{{- define "ix.v1.common.lib.service.primaryValidation" -}} + + {{/* Initialize values */}} + {{- $hasPrimary := false -}} + {{- $hasEnabled := false -}} + + {{- range $name, $service := .Values.service -}} + + {{/* If service is enabled */}} + {{- if $service.enabled -}} + {{- $hasEnabled = true -}} + + {{/* And service is primary */}} + {{- if and (hasKey $service "primary") ($service.primary) -}} + {{/* Fail if there is already a primary service */}} + {{- if $hasPrimary -}} + {{- fail "Service - Only one service can be primary" -}} + {{- end -}} + + {{- $hasPrimary = true -}} + + {{- include "ix.v1.common.lib.servicePort.primaryValidation" (dict "objectData" $service.ports) -}} + + {{- end -}} + + {{- end -}} + {{- end -}} + + {{/* Require at least one primary service, if any enabled */}} + {{- if and $hasEnabled (not $hasPrimary) -}} + {{- fail "Service - At least one enabled service must be primary" -}} + {{- end -}} + +{{- end -}} + +{{/* Service Port Primary Validation */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.service.primaryValidation" (dict "objectData" $objectData -}} +objectData: + The ports of the service. +*/}} + +{{- define "ix.v1.common.lib.servicePort.primaryValidation" -}} + {{- $objectData := .objectData -}} + + {{/* Initialize values */}} + {{- $hasPrimary := false -}} + {{- $hasEnabled := false -}} + + {{- range $name, $port := $objectData -}} + + {{/* If service is enabled */}} + {{- if $port.enabled -}} + {{- $hasEnabled = true -}} + + {{/* And service is primary */}} + {{- if and (hasKey $port "primary") ($port.primary) -}} + + {{/* Fail if there is already a primary port */}} + {{- if $hasPrimary -}} + {{- fail "Service - Only one port per service can be primary" -}} + {{- end -}} + + {{- $hasPrimary = true -}} + + {{- end -}} + + {{- end -}} + {{- end -}} + + {{/* Require at least one primary service, if any enabled */}} + {{- if and $hasEnabled (not $hasPrimary) -}} + {{- fail "Service - At least one enabled port in service must be primary" -}} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/service/serviceTypeConfig/_cluster_ip.tpl b/library/common/1.0.0/templates/lib/service/serviceTypeConfig/_cluster_ip.tpl new file mode 100644 index 0000000000..55458b9f8b --- /dev/null +++ b/library/common/1.0.0/templates/lib/service/serviceTypeConfig/_cluster_ip.tpl @@ -0,0 +1,16 @@ +{{/* Service - clusterIP */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.service.clusterIP" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} +rootCtx: The root context of the chart. +objectData: The service object data +*/}} + +{{- define "ix.v1.common.lib.service.clusterIP" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData }} + + {{- with $objectData.clusterIP }} +clusterIP: {{ tpl . $rootCtx }} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/service/serviceTypeConfig/_externalIPs.tpl b/library/common/1.0.0/templates/lib/service/serviceTypeConfig/_externalIPs.tpl new file mode 100644 index 0000000000..7ac47abe03 --- /dev/null +++ b/library/common/1.0.0/templates/lib/service/serviceTypeConfig/_externalIPs.tpl @@ -0,0 +1,17 @@ +{{/* Service - externalIPs */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.service.externalIPs" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} +rootCtx: The root context of the chart. +objectData: The service object data +*/}} + +{{- define "ix.v1.common.lib.service.externalIPs" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- with $objectData.externalIPs -}} + {{- range . }} +- {{ tpl . $rootCtx }} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/service/serviceTypeConfig/_externalTrafficPolicy.tpl b/library/common/1.0.0/templates/lib/service/serviceTypeConfig/_externalTrafficPolicy.tpl new file mode 100644 index 0000000000..e7e1333b7d --- /dev/null +++ b/library/common/1.0.0/templates/lib/service/serviceTypeConfig/_externalTrafficPolicy.tpl @@ -0,0 +1,22 @@ +{{/* Service - externalTrafficPolicy */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.service.externalTrafficPolicy" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} +rootCtx: The root context of the chart. +objectData: The service object data +*/}} + +{{- define "ix.v1.common.lib.service.externalTrafficPolicy" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData }} + + {{- with $objectData.externalTrafficPolicy }} + {{- $policy := tpl . $rootCtx -}} + {{- $policies := (list "Cluster" "Local") -}} + + {{- if not (mustHas $policy $policies) -}} + {{- fail (printf "Service - Expected to be one of [%s], but got [%s]" (join ", " $policies) $policy) -}} + {{- end }} +externalTrafficPolicy: {{ $policy }} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/service/serviceTypeConfig/_ipFamily.tpl b/library/common/1.0.0/templates/lib/service/serviceTypeConfig/_ipFamily.tpl new file mode 100644 index 0000000000..c10e324d10 --- /dev/null +++ b/library/common/1.0.0/templates/lib/service/serviceTypeConfig/_ipFamily.tpl @@ -0,0 +1,38 @@ +{{/* Service - ipFamily */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.service.ipFamily" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} +rootCtx: The root context of the chart. +objectData: The service object data +*/}} + +{{- define "ix.v1.common.lib.service.ipFamily" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- with $objectData.ipFamilyPolicy -}} + {{- $famPolicy := tpl . $rootCtx -}} + + {{- $stacks := (list "SingleStack" "PreferDualStack" "RequireDualStack") -}} + {{- if not (mustHas $famPolicy $stacks) -}} + {{- fail (printf "Service - Expected to be one of [%s], but got [%s]" (join ", " $stacks) $famPolicy) -}} + {{- end }} +ipFamilyPolicy: {{ $famPolicy }} + {{- end -}} + + {{- if and $objectData.ipFamilies (not (kindIs "slice" $objectData.ipFamilies)) -}} + {{- fail (printf "Service - Expected to be a list, but got a [%s]" (kindOf $objectData.ipFamilies)) -}} + {{- end -}} + + {{- with $objectData.ipFamilies }} +ipFamilies: + {{- range . }} + {{- $ipFam := tpl . $rootCtx -}} + + {{- $stacks := (list "IPv4" "IPv6") -}} + {{- if not (mustHas $ipFam $stacks) -}} + {{- fail (printf "Service - Expected to be one of [%s], but got [%s]" (join ", " $stacks) $ipFam) -}} + {{- end }} + - {{ $ipFam }} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/service/serviceTypeConfig/_publishNotReadyAddresses.tpl b/library/common/1.0.0/templates/lib/service/serviceTypeConfig/_publishNotReadyAddresses.tpl new file mode 100644 index 0000000000..dc828feba0 --- /dev/null +++ b/library/common/1.0.0/templates/lib/service/serviceTypeConfig/_publishNotReadyAddresses.tpl @@ -0,0 +1,19 @@ +{{/* Service - publishNotReadyAddresses */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.service.publishNotReadyAddresses" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} +rootCtx: The root context of the chart. +objectData: The service object data +*/}} + +{{- define "ix.v1.common.lib.service.publishNotReadyAddresses" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData }} + + {{- $publishAddr := false -}} + + {{- if (kindIs "bool" $objectData.publishNotReadyAddresses) -}} + {{- $publishAddr = $objectData.publishNotReadyAddresses -}} + {{- end -}} + + {{- $publishAddr -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/service/serviceTypeConfig/_sessionAffinity.tpl b/library/common/1.0.0/templates/lib/service/serviceTypeConfig/_sessionAffinity.tpl new file mode 100644 index 0000000000..72867637d1 --- /dev/null +++ b/library/common/1.0.0/templates/lib/service/serviceTypeConfig/_sessionAffinity.tpl @@ -0,0 +1,42 @@ +{{/* Service - Session Affinity */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.service.sessionAffinity" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} +rootCtx: The root context of the chart. +objectData: The service object data +*/}} + +{{- define "ix.v1.common.lib.service.sessionAffinity" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- with $objectData.sessionAffinity -}} + {{- $affinity := tpl . $rootCtx -}} + {{- $affinities := (list "ClientIP" "None") -}} + {{- if not (mustHas $affinity $affinities) -}} + {{- fail (printf "Service - Expected to be one of [%s], but got [%s]" (join ", " $affinities) $affinity) -}} + {{- end }} +sessionAffinity: {{ $affinity }} + {{- if eq $affinity "ClientIP" -}} + {{- with $objectData.sessionAffinityConfig -}} + {{- with .clientIP -}} + + {{- $timeout := .timeoutSeconds -}} + {{- if kindIs "string" $timeout -}} + {{- $timeout = tpl $timeout $rootCtx -}} + {{- end -}} + + {{- $timeout = int $timeout -}} + {{- if and $timeout (mustHas (kindOf $timeout) (list "float64" "int")) -}} + {{- if or (lt $timeout 0) (gt $timeout 86400) -}} + {{- fail (printf "Service - Expected to be between [0 - 86400], but got [%v]" $timeout) -}} + {{- end }} +sessionAffinityConfig: + clientIP: + timeoutSeconds: {{ $timeout }} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/service/serviceTypeSpecs/_clusterIP.tpl b/library/common/1.0.0/templates/lib/service/serviceTypeSpecs/_clusterIP.tpl new file mode 100644 index 0000000000..09933b57bd --- /dev/null +++ b/library/common/1.0.0/templates/lib/service/serviceTypeSpecs/_clusterIP.tpl @@ -0,0 +1,21 @@ +{{/* Service - ClusterIP Spec */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.service.spec.clusterIP" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} +rootCtx: The root context of the chart. +objectData: The service object data +*/}} + +{{- define "ix.v1.common.lib.service.spec.clusterIP" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData }} + +type: ClusterIP +publishNotReadyAddresses: {{ include "ix.v1.common.lib.service.publishNotReadyAddresses" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim }} + {{- with (include "ix.v1.common.lib.service.externalIPs" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim) }} +externalIPs: + {{- . | nindent 2 }} + {{- end -}} + {{- include "ix.v1.common.lib.service.sessionAffinity" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 0 }} + {{- include "ix.v1.common.lib.service.clusterIP" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 0 }} + {{- include "ix.v1.common.lib.service.ipFamily" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 0 }} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/service/serviceTypeSpecs/_nodePort.tpl b/library/common/1.0.0/templates/lib/service/serviceTypeSpecs/_nodePort.tpl new file mode 100644 index 0000000000..ec58fb28e2 --- /dev/null +++ b/library/common/1.0.0/templates/lib/service/serviceTypeSpecs/_nodePort.tpl @@ -0,0 +1,22 @@ +{{/* Service - NodePort Spec */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.service.spec.nodePort" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} +rootCtx: The root context of the chart. +objectData: The service object data +*/}} + +{{- define "ix.v1.common.lib.service.spec.nodePort" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData }} + +type: NodePort +publishNotReadyAddresses: {{ include "ix.v1.common.lib.service.publishNotReadyAddresses" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim }} + {{- with (include "ix.v1.common.lib.service.externalIPs" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim) }} +externalIPs: + {{- . | nindent 2 }} + {{- end -}} + {{- include "ix.v1.common.lib.service.sessionAffinity" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 0 }} + {{- include "ix.v1.common.lib.service.clusterIP" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 0 }} + {{- include "ix.v1.common.lib.service.ipFamily" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 0 }} + {{- include "ix.v1.common.lib.service.externalTrafficPolicy" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 0 }} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/serviceAccount/_validation.tpl b/library/common/1.0.0/templates/lib/serviceAccount/_validation.tpl new file mode 100644 index 0000000000..62b40a5d04 --- /dev/null +++ b/library/common/1.0.0/templates/lib/serviceAccount/_validation.tpl @@ -0,0 +1,38 @@ +{{/* Service Account Primary Validation */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.serviceAccount.primaryValidation" $ -}} +*/}} + +{{- define "ix.v1.common.lib.serviceAccount.primaryValidation" -}} + + {{/* Initialize values */}} + {{- $hasPrimary := false -}} + {{- $hasEnabled := false -}} + + {{- range $name, $serviceAccount := .Values.serviceAccount -}} + + {{/* If service account is enabled */}} + {{- if $serviceAccount.enabled -}} + {{- $hasEnabled = true -}} + + {{/* And service account is primary */}} + {{- if and (hasKey $serviceAccount "primary") ($serviceAccount.primary) -}} + + {{/* Fail if there is already a primary service account */}} + {{- if $hasPrimary -}} + {{- fail "Service Account - Only one service account can be primary" -}} + {{- end -}} + + {{- $hasPrimary = true -}} + + {{- end -}} + + {{- end -}} + {{- end -}} + + {{/* Require at least one primary service account, if any enabled */}} + {{- if and $hasEnabled (not $hasPrimary) -}} + {{- fail "Service Account - At least one enabled service account must be primary" -}} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/storage/_validation.tpl b/library/common/1.0.0/templates/lib/storage/_validation.tpl new file mode 100644 index 0000000000..f78903f6c5 --- /dev/null +++ b/library/common/1.0.0/templates/lib/storage/_validation.tpl @@ -0,0 +1,22 @@ +{{/* Persistence Validation */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.persistence.validation" (dict "objectData" $objectData) -}} +objectData: + rootCtx: The root context of the chart. + objectData: The persistence object. +*/}} + +{{- define "ix.v1.common.lib.persistence.validation" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $types := (list "emptyDir" "hostPath" "ixVolume" "secret" "configmap" "device") -}} + {{- if not (mustHas $objectData.type $types) -}} + {{- fail (printf "Persistence - Expected to be one of [%s], but got [%s]" (join ", " $types) $objectData.type) -}} + {{- end -}} + + {{- if and $objectData.targetSelector (not (kindIs "map" $objectData.targetSelector)) -}} + {{- fail (printf "Persistence - Expected to be [dict], but got [%s]" (kindOf $objectData.targetSelector)) -}} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/workload/_cronjobSpec.tpl b/library/common/1.0.0/templates/lib/workload/_cronjobSpec.tpl new file mode 100644 index 0000000000..b8bff4c767 --- /dev/null +++ b/library/common/1.0.0/templates/lib/workload/_cronjobSpec.tpl @@ -0,0 +1,26 @@ +{{/* CronJob Spec */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.workload.cronjobSpec" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} +rootCtx: The root context of the chart. +objectData: + schedule: The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. + concurrencyPolicy: Allow, Forbid, or Replace. Defaults to Allow. + failedJobsHistoryLimit: The number of failed finished jobs to retain. Defaults to 1. + successfulJobsHistoryLimit: The number of successful finished jobs to retain. Defaults to 3. + startingDeadlineSeconds: Optional deadline in seconds for starting the job if it misses scheduled time for any reason. Defaults to nil. + timezone: The timezone name. Defaults to .Values.TZ + +jobSpec data +*/}} +{{- define "ix.v1.common.lib.workload.cronjobSpec" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} +timeZone: {{ (tpl ($objectData.timezone | default $rootCtx.Values.TZ) $rootCtx) | quote }} +schedule: {{ (tpl $objectData.schedule $rootCtx) | quote }} +concurrencyPolicy: {{ $objectData.concurrencyPolicy | default "Forbid" }} +failedJobsHistoryLimit: {{ $objectData.failedJobsHistoryLimit | default 1 }} +successfulJobsHistoryLimit: {{ $objectData.successfulJobsHistoryLimit | default 3 }} +startingDeadlineSeconds: {{ $objectData.startingDeadlineSeconds | default nil }} +jobTemplate: + spec: + {{- include "ix.v1.common.lib.workload.jobSpec" (dict "rootCtx" $rootCtx "objectData" $objectData) | nindent 4 }} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/workload/_deployementSpec.tpl b/library/common/1.0.0/templates/lib/workload/_deployementSpec.tpl new file mode 100644 index 0000000000..8cc5b27b02 --- /dev/null +++ b/library/common/1.0.0/templates/lib/workload/_deployementSpec.tpl @@ -0,0 +1,29 @@ +{{/* Deployment Spec */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.workload.deploymentSpec" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} +rootCtx: The root context of the chart. +objectData: + replicas: The number of replicas. + revisionHistoryLimit: The number of old ReplicaSets to retain to allow rollback. + strategy: The deployment strategy to use to replace existing pods with new ones. +*/}} +{{- define "ix.v1.common.lib.workload.deploymentSpec" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} + {{- $strategy := $objectData.strategy | default "Recreate" }} +replicas: {{ $objectData.replicas | default 1 }} +revisionHistoryLimit: {{ $objectData.revisionHistoryLimit | default 3 }} +strategy: + type: {{ $strategy }} + {{- if and (eq $objectData.strategy "RollingUpdate") $objectData.rollingUpdate -}} + {{ if (or (hasKey $objectData.rollingUpdate "maxUnavailable") (hasKey $objectData.rollingUpdate "maxSurge")) }} + rollingUpdate: + {{- if hasKey $objectData.rollingUpdate "maxUnavailable" }} + maxUnavailable: {{ $objectData.rollingUpdate.maxUnavailable }} + {{- end -}} + {{- if hasKey $objectData.rollingUpdate "maxSurge" }} + maxSurge: {{ $objectData.rollingUpdate.maxSurge }} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/workload/_jobSpec.tpl b/library/common/1.0.0/templates/lib/workload/_jobSpec.tpl new file mode 100644 index 0000000000..6ed51c66c5 --- /dev/null +++ b/library/common/1.0.0/templates/lib/workload/_jobSpec.tpl @@ -0,0 +1,24 @@ +{{/* Job Spec */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.workload.jobSpec" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} +rootCtx: The root context of the chart. +objectData: + backoffLimit: The number of retries before marking this job failed. Defaults to 6. + completions: The desired number of successfully finished pods the job should be run with. Defaults to 1. + parallelism: The maximum desired number of pods the job should run at any given time. Defaults to 1. + activeDeadlineSeconds: Specifies the duration in seconds relative to the startTime that the job may be active before the system tries to terminate it; value must be positive integer. If set to nil, the job is never terminated due to timeout. + ttlSecondsAfterFinished: TTLSecondsAfterFinished limits the lifetime of a Job that has finished execution (either Complete or Failed). If this field is set, ttlSecondsAfterFinished after the Job finishes, it is eligible to be automatically deleted. When the Job is being deleted, its lifecycle guarantees (e.g. finalizers) will be honored. If this field is unset, the Job won't be automatically deleted. If this field is set to zero, the Job becomes eligible to be deleted immediately after it finishes. This field is alpha-level and is only honored by servers that enable the TTLAfterFinished feature. + completionMode: CompletionMode specifies how Pod completions are tracked. It can be `NonIndexed` (default) or `Indexed`. +*/}} +{{- define "ix.v1.common.lib.workload.jobSpec" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} +backoffLimit: {{ $objectData.backoffLimit | default 5 }} +completionMode: {{ $objectData.completionMode | default "NonIndexed" }} +completions: {{ $objectData.completions | default nil }} +parallelism: {{ $objectData.parallelism | default 1 }} +ttlSecondsAfterFinished: {{ $objectData.ttlSecondsAfterFinished | default 120 }} + {{- with $objectData.activeDeadlineSeconds }} +activeDeadlineSeconds: {{ . }} + {{- end -}} +{{- 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 new file mode 100644 index 0000000000..64467bd85a --- /dev/null +++ b/library/common/1.0.0/templates/lib/workload/_pod.tpl @@ -0,0 +1,49 @@ +{{/* Pod Spec */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.workload.pod" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the Pod. +*/}} +{{- define "ix.v1.common.lib.workload.pod" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData }} +serviceAccountName: {{ include "ix.v1.common.lib.pod.serviceAccountName" (dict "rootCtx" $rootCtx "objectData" $objectData) }} +automountServiceAccountToken: {{ include "ix.v1.common.lib.pod.automountServiceAccountToken" (dict "rootCtx" $rootCtx "objectData" $objectData) }} +runtimeClassName: {{ include "ix.v1.common.lib.pod.runtimeClassName" (dict "rootCtx" $rootCtx "objectData" $objectData) }} + {{- with (include "ix.v1.common.lib.pod.imagePullSecret" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim) }} +imagePullSecrets: + {{- . | nindent 2 }} + {{- end }} +hostNetwork: {{ include "ix.v1.common.lib.pod.hostNetwork" (dict "rootCtx" $rootCtx "objectData" $objectData) }} +enableServiceLinks: {{ include "ix.v1.common.lib.pod.enableServiceLinks" (dict "rootCtx" $rootCtx "objectData" $objectData) }} +restartPolicy: {{ include "ix.v1.common.lib.pod.restartPolicy" (dict "rootCtx" $rootCtx "objectData" $objectData) }} + {{- with (include "ix.v1.common.lib.pod.hostAliases" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim) }} +hostAliases: + {{- . | nindent 2 }} + {{- end -}} + {{- with (include "ix.v1.common.lib.pod.hostname" (dict "rootCtx" $rootCtx "objectData" $objectData)) }} +hostname: {{ . }} + {{- end -}} + {{- include "ix.v1.common.lib.pod.dns" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} + {{- with (include "ix.v1.common.lib.pod.terminationGracePeriodSeconds" (dict "rootCtx" $rootCtx "objectData" $objectData)) }} +terminationGracePeriodSeconds: {{ . }} + {{- end -}} + {{- with (include "ix.v1.common.lib.pod.tolerations" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim) }} +tolerations: + {{- . | nindent 2 }} + {{- end }} +securityContext: + {{- include "ix.v1.common.lib.pod.securityContext" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 2 }} + {{- if $objectData.podSpec.containers }} +containers: + {{- include "ix.v1.common.lib.pod.containerSpawner" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 2 -}} + {{- end -}} + {{- if $objectData.podSpec.initContainers }} +initContainers: + {{- include "ix.v1.common.lib.pod.initContainerSpawner" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim | nindent 2 -}} + {{- end -}} + {{- with (include "ix.v1.common.lib.pod.volumes" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim) }} +volumes: + {{- . | nindent 2 }} +{{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/workload/validation/_cronjobValidation.tpl b/library/common/1.0.0/templates/lib/workload/validation/_cronjobValidation.tpl new file mode 100644 index 0000000000..923da0acd0 --- /dev/null +++ b/library/common/1.0.0/templates/lib/workload/validation/_cronjobValidation.tpl @@ -0,0 +1,29 @@ +{{/* CronJob Validation */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.workload.cronjobValidation" (dict "objectData" $objectData) -}} +rootCtx: The root context of the chart. +objectData: + completionMode: The completionMode of the object. + completions: The completions of the object. + parallelism: The parallelism of the object. +*/}} +{{- define "ix.v1.common.lib.workload.cronjobValidation" -}} + {{- $objectData := .objectData -}} + + {{- if $objectData.concurrencyPolicy -}} + {{- $concurrencyPolicy := $objectData.concurrencyPolicy -}} + + {{- $policies := (list "Allow" "Forbid" "Replace") -}} + {{- if not (mustHas $concurrencyPolicy $policies) -}} + {{- fail (printf "CronJob - Expected to be one of [%s], but got [%v]" (join ", " $policies) $concurrencyPolicy) -}} + {{- end -}} + + {{- end -}} + + {{- if not $objectData.schedule -}} + {{- fail "CronJob - Expected non-empty " -}} + {{- end -}} + + {{/* CronJob contains a job inside, so we validate job values too */}} + {{- include "ix.v1.common.lib.workload.jobValidation" (dict "objectData" $objectData) -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/workload/validation/_deploymentValidation.tpl b/library/common/1.0.0/templates/lib/workload/validation/_deploymentValidation.tpl new file mode 100644 index 0000000000..e35208ee8d --- /dev/null +++ b/library/common/1.0.0/templates/lib/workload/validation/_deploymentValidation.tpl @@ -0,0 +1,30 @@ +{{/* Deployment Validation */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.workload.deploymentValidation" (dict "objectData" $objectData) -}} +rootCtx: The root context of the chart. +objectData: + strategy: The strategy of the object. + rollingUpdate: The rollingUpdate of the object. +*/}} +{{- define "ix.v1.common.lib.workload.deploymentValidation" -}} + {{- $objectData := .objectData -}} + + {{- if $objectData.strategy -}} + {{- $strategy := $objectData.strategy -}} + + {{- $strategies := (list "Recreate" "RollingUpdate") -}} + {{- if not (mustHas $strategy $strategies) -}} + {{- fail (printf "Deployment - Expected to be one of [%s], but got [%v]" (join ", " $strategies) $strategy) -}} + {{- end -}} + + {{- end -}} + + {{- if $objectData.rollingUpdate -}} + {{- $rollUp := $objectData.rollingUpdate -}} + + {{- if and $rollUp (not (kindIs "map" $rollUp)) -}} + {{- fail (printf "Deployment - Expected to be a dictionary, but got [%v]" (kindOf $rollUp)) -}} + {{- end -}} + + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/workload/validation/_jobValidation.tpl b/library/common/1.0.0/templates/lib/workload/validation/_jobValidation.tpl new file mode 100644 index 0000000000..aee63c5c7e --- /dev/null +++ b/library/common/1.0.0/templates/lib/workload/validation/_jobValidation.tpl @@ -0,0 +1,32 @@ +{{/* Job Validation */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.workload.jobValidation" (dict "objectData" $objectData) -}} +rootCtx: The root context of the chart. +objectData: + completionMode: The completionMode of the object. + completions: The completions of the object. + parallelism: The parallelism of the object. +*/}} +{{- define "ix.v1.common.lib.workload.jobValidation" -}} + {{- $objectData := .objectData -}} + + {{- if $objectData.completionMode -}} + {{- $completionMode := $objectData.completionMode -}} + + {{- if not (mustHas $completionMode (list "Indexed" "NonIndexed")) -}} + {{- fail (printf "Job - Expected to be one of [Indexed, NonIndexed], but got [%v]" $completionMode) -}} + {{- end -}} + + {{- if eq $completionMode "Indexed" -}} + {{- if not $objectData.completions -}} + {{- fail "Job - Expected to be set when is set to [Indexed]" -}} + {{- end -}} + + {{- if not $objectData.parallelism -}} + {{- fail "Job - Expected to be set when is set to [Indexed]" -}} + {{- end -}} + {{- end -}} + + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/workload/validation/_workloadValidation.tpl b/library/common/1.0.0/templates/lib/workload/validation/_workloadValidation.tpl new file mode 100644 index 0000000000..506a0a810f --- /dev/null +++ b/library/common/1.0.0/templates/lib/workload/validation/_workloadValidation.tpl @@ -0,0 +1,43 @@ +{{/* Workload Basic Validation */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.workload.primaryValidation" $ -}} +*/}} +{{- define "ix.v1.common.lib.workload.primaryValidation" -}} + + {{/* Initialize values */}} + {{- $hasPrimary := false -}} + {{- $hasEnabled := false -}} + + {{/* Go over workload */}} + {{- range $name, $workload := .Values.workload -}} + + {{/* If workload is enabled */}} + {{- if $workload.enabled -}} + + {{- $types := (list "Deployment" "Job" "CronJob") -}} + {{- if not (mustHas $workload.type $types) -}} + {{- fail (printf "Workload - Expected to be one of [%s], but got [%s]" (join ", " $types) $workload.type) -}} + {{- end -}} + + {{- $hasEnabled = true -}} + + {{/* And workload is primary */}} + {{- if $workload.primary -}} + {{/* Fail if there is already a primary workload */}} + {{- if $hasPrimary -}} + {{- fail "Workload - Only one workload can be primary" -}} + {{- end -}} + + {{- $hasPrimary = true -}} + + {{- end -}} + {{- end -}} + + {{- end -}} + + {{/* Require at one primary workload, if any enabled */}} + {{- if and $hasEnabled (not $hasPrimary) -}} + {{- fail "Workload - One enabled workload must be primary" -}} + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/loader/_all.tpl b/library/common/1.0.0/templates/loader/_all.tpl new file mode 100644 index 0000000000..81a1667144 --- /dev/null +++ b/library/common/1.0.0/templates/loader/_all.tpl @@ -0,0 +1,8 @@ +{{/* Main entrypoint for the library */}} +{{- define "ix.v1.common.loader.all" -}} + + {{- include "ix.v1.common.loader.init" . -}} + + {{- include "ix.v1.common.loader.apply" . -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/loader/_apply.tpl b/library/common/1.0.0/templates/loader/_apply.tpl new file mode 100644 index 0000000000..c04339eaa4 --- /dev/null +++ b/library/common/1.0.0/templates/loader/_apply.tpl @@ -0,0 +1,31 @@ +{{/* Loads all spawners */}} +{{- define "ix.v1.common.loader.apply" -}} + + {{/* Render ConfigMap(s) */}} + {{- include "ix.v1.common.spawner.configmap" . | nindent 0 -}} + + {{/* Render Certificate(s) */}} + {{- include "ix.v1.common.spawner.certificate" . | nindent 0 -}} + + {{/* Render Secret(s) */}} + {{- include "ix.v1.common.spawner.secret" . | nindent 0 -}} + + {{/* Render Image Pull Secrets(s) */}} + {{- include "ix.v1.common.spawner.imagePullSecret" . | nindent 0 -}} + + {{/* Render Service Accounts(s) */}} + {{- include "ix.v1.common.spawner.serviceAccount" . | nindent 0 -}} + + {{/* Render RBAC(s) */}} + {{- include "ix.v1.common.spawner.rbac" . | nindent 0 -}} + + {{/* Render External Interface(s) */}} + {{- include "ix.v1.common.spawner.externalInterface" . | nindent 0 -}} + + {{/* Render Workload(s) */}} + {{- include "ix.v1.common.spawner.workload" . | nindent 0 -}} + + {{/* Render Services(s) */}} + {{- include "ix.v1.common.spawner.service" . | nindent 0 -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/loader/_init.tpl b/library/common/1.0.0/templates/loader/_init.tpl new file mode 100644 index 0000000000..e39981f32c --- /dev/null +++ b/library/common/1.0.0/templates/loader/_init.tpl @@ -0,0 +1,8 @@ +{{/* Initialiaze values of the chart */}} + +{{- define "ix.v1.common.loader.init" -}} + + {{/* Merge chart values and the common chart defaults */}} + {{- include "ix.v1.common.values.init" . -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/spawner/_certificate.tpl b/library/common/1.0.0/templates/spawner/_certificate.tpl new file mode 100644 index 0000000000..afd9b82fbb --- /dev/null +++ b/library/common/1.0.0/templates/spawner/_certificate.tpl @@ -0,0 +1,39 @@ +{{/* Certificate Spawwner */}} +{{/* Call this template: +{{ include "ix.v1.common.spawner.certificate" $ -}} +*/}} + +{{- define "ix.v1.common.spawner.certificate" -}} + + {{- range $name, $certificate := .Values.scaleCertificate -}} + + {{- if $certificate.enabled -}} + + {{/* Create a copy of the certificate */}} + {{- $objectData := (mustDeepCopy $certificate) -}} + + {{- $objectName := (printf "%s-%s" (include "ix.v1.common.lib.chart.names.fullname" $) $name) -}} + {{/* Perform validations */}} + {{- include "ix.v1.common.lib.chart.names.validation" (dict "name" $objectName) -}} + {{- include "ix.v1.common.lib.certificate.validation" (dict "objectData" $objectData) -}} + {{- include "ix.v1.common.lib.metadata.validation" (dict "objectData" $objectData "caller" "Certificate") -}} + + {{/* Prepare data */}} + {{- $data := fromJson (include "ix.v1.common.lib.certificate.getData" (dict "rootCtx" $ "objectData" $objectData)) -}} + {{- $_ := set $objectData "data" $data -}} + + {{/* Set the type to certificate */}} + {{- $_ := set $objectData "type" "certificate" -}} + + {{/* Set the name of the certificate */}} + {{- $_ := set $objectData "name" $objectName -}} + {{- $_ := set $objectData "shortName" $name -}} + + {{/* Call class to create the object */}} + {{- include "ix.v1.common.class.secret" (dict "rootCtx" $ "objectData" $objectData) -}} + + {{- end -}} + + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/spawner/_configmap.tpl b/library/common/1.0.0/templates/spawner/_configmap.tpl new file mode 100644 index 0000000000..53df70d0f8 --- /dev/null +++ b/library/common/1.0.0/templates/spawner/_configmap.tpl @@ -0,0 +1,32 @@ +{{/* Configmap Spawwner */}} +{{/* Call this template: +{{ include "ix.v1.common.spawner.configmap" $ -}} +*/}} + +{{- define "ix.v1.common.spawner.configmap" -}} + + {{- range $name, $configmap := .Values.configmap -}} + + {{- if $configmap.enabled -}} + + {{/* Create a copy of the configmap */}} + {{- $objectData := (mustDeepCopy $configmap) -}} + + {{- $objectName := (printf "%s-%s" (include "ix.v1.common.lib.chart.names.fullname" $) $name) -}} + {{/* Perform validations */}} + {{- include "ix.v1.common.lib.chart.names.validation" (dict "name" $objectName) -}} + {{- include "ix.v1.common.lib.configmap.validation" (dict "objectData" $objectData) -}} + {{- include "ix.v1.common.lib.metadata.validation" (dict "objectData" $objectData "caller" "ConfigMap") -}} + + {{/* Set the name of the configmap */}} + {{- $_ := set $objectData "name" $objectName -}} + {{- $_ := set $objectData "shortName" $name -}} + + {{/* Call class to create the object */}} + {{- include "ix.v1.common.class.configmap" (dict "rootCtx" $ "objectData" $objectData) -}} + + {{- end -}} + + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/spawner/_externalInterface.tpl b/library/common/1.0.0/templates/spawner/_externalInterface.tpl new file mode 100644 index 0000000000..19ce301787 --- /dev/null +++ b/library/common/1.0.0/templates/spawner/_externalInterface.tpl @@ -0,0 +1,32 @@ +{{/* External Interface Spawwner */}} +{{/* Call this template: +{{ include "ix.v1.common.spawner.externalInterface" $ -}} +*/}} + +{{- define "ix.v1.common.spawner.externalInterface" -}} + + {{- range $iface := .Values.scaleExternalInterface -}} + {{- include "ix.v1.common.lib.externalInterface.validation" (dict "objectData" $iface) -}} + {{- end -}} + + {{/* Now we have validated interfaces, render the objects */}} + + {{- range $index, $interface := .Values.ixExternalInterfacesConfiguration -}} + + {{- $objectData := dict -}} + {{/* Create a copy of the interface and put it in objectData.config */}} + {{- $_ := set $objectData "config" (mustDeepCopy $interface) -}} + + {{- $objectName := (printf "ix-%s-%v" $.Release.Name $index) -}} + {{/* Perform validations */}} + {{- include "ix.v1.common.lib.chart.names.validation" (dict "name" $objectName) -}} + + {{/* Set the name of the object to objectData.name */}} + {{- $_ := set $objectData "name" $objectName -}} + + {{/* Call class to create the object */}} + {{- include "ix.v1.common.class.networkAttachmentDefinition" (dict "rootCtx" $ "objectData" $objectData) -}} + + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/spawner/_imagePullSecret.tpl b/library/common/1.0.0/templates/spawner/_imagePullSecret.tpl new file mode 100644 index 0000000000..19a8817601 --- /dev/null +++ b/library/common/1.0.0/templates/spawner/_imagePullSecret.tpl @@ -0,0 +1,40 @@ +{{/* Image Pull Secrets Spawner */}} +{{/* Call this template: +{{ include "ix.v1.common.spawner.imagePullSecret" $ -}} +*/}} + +{{- define "ix.v1.common.spawner.imagePullSecret" -}} + + {{- range $name, $imgPullSecret := .Values.imagePullSecret -}} + + {{- if $imgPullSecret.enabled -}} + + {{/* Create a copy of the configmap */}} + {{- $objectData := (mustDeepCopy $imgPullSecret) -}} + + {{- $objectName := (printf "%s-%s" (include "ix.v1.common.lib.chart.names.fullname" $) $name) -}} + + {{/* Perform validations */}} + {{- include "ix.v1.common.lib.chart.names.validation" (dict "name" $objectName) -}} + {{- include "ix.v1.common.lib.imagePullSecret.validation" (dict "objectData" $objectData) -}} + {{- include "ix.v1.common.lib.metadata.validation" (dict "objectData" $objectData "caller" "Image Pull Secret") -}} + {{- $data := include "ix.v1.common.lib.imagePullSecret.createData" (dict "rootCtx" $ "objectData" $objectData) -}} + + {{/* Update the data */}} + {{- $_ := set $objectData "data" $data -}} + + {{/* Set the type to Image Pull Secret */}} + {{- $_ := set $objectData "type" "imagePullSecret" -}} + + {{/* Set the name of the image pull secret */}} + {{- $_ := set $objectData "name" $objectName -}} + {{- $_ := set $objectData "shortName" $name -}} + + {{/* Call class to create the object */}} + {{- include "ix.v1.common.class.secret" (dict "rootCtx" $ "objectData" $objectData) -}} + + {{- end -}} + + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/spawner/_rbac.tpl b/library/common/1.0.0/templates/spawner/_rbac.tpl new file mode 100644 index 0000000000..3f0902fb75 --- /dev/null +++ b/library/common/1.0.0/templates/spawner/_rbac.tpl @@ -0,0 +1,43 @@ +{{/* RBAC Spawner */}} +{{/* Call this template: +{{ include "ix.v1.common.spawner.rbac" $ -}} +*/}} + +{{- define "ix.v1.common.spawner.rbac" -}} + + {{/* Primary validation for enabled rbacs. */}} + {{- include "ix.v1.common.lib.rbac.primaryValidation" $ -}} + + {{- range $name, $rbac := .Values.rbac -}} + + {{- if $rbac.enabled -}} + + {{/* Create a copy of the configmap */}} + {{- $objectData := (mustDeepCopy $rbac) -}} + + {{- $objectName := include "ix.v1.common.lib.chart.names.fullname" $ -}} + {{- if not $objectData.primary -}} + {{- $objectName = (printf "%s-%s" (include "ix.v1.common.lib.chart.names.fullname" $) $name) -}} + {{- end -}} + + {{/* Perform validations */}} + {{- include "ix.v1.common.lib.chart.names.validation" (dict "name" $objectName) -}} + {{- include "ix.v1.common.lib.metadata.validation" (dict "objectData" $objectData "caller" "RBAC") -}} + + {{/* Set the name of the rbac */}} + {{- $_ := set $objectData "name" $objectName -}} + {{- $_ := set $objectData "shortName" $name -}} + + {{/* If clusteWide key does not exist, assume false */}} + {{- if not (hasKey $objectData "clusterWide") -}} + {{- $_ := set $objectData "clusterWide" false -}} + {{- end -}} + + {{/* Call class to create the object */}} + {{- include "ix.v1.common.class.rbac" (dict "rootCtx" $ "objectData" $objectData) -}} + + {{- end -}} + + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/spawner/_secret.tpl b/library/common/1.0.0/templates/spawner/_secret.tpl new file mode 100644 index 0000000000..b8f53f1d18 --- /dev/null +++ b/library/common/1.0.0/templates/spawner/_secret.tpl @@ -0,0 +1,32 @@ +{{/* Secret Spawwner */}} +{{/* Call this template: +{{ include "ix.v1.common.spawner.secret" $ -}} +*/}} + +{{- define "ix.v1.common.spawner.secret" -}} + + {{- range $name, $secret := .Values.secret -}} + + {{- if $secret.enabled -}} + + {{/* Create a copy of the secret */}} + {{- $objectData := (mustDeepCopy $secret) -}} + + {{- $objectName := (printf "%s-%s" (include "ix.v1.common.lib.chart.names.fullname" $) $name) -}} + {{/* Perform validations */}} + {{- include "ix.v1.common.lib.chart.names.validation" (dict "name" $objectName) -}} + {{- include "ix.v1.common.lib.secret.validation" (dict "objectData" $objectData) -}} + {{- include "ix.v1.common.lib.metadata.validation" (dict "objectData" $objectData "caller" "Secret") -}} + + {{/* Set the name of the secret */}} + {{- $_ := set $objectData "name" $objectName -}} + {{- $_ := set $objectData "shortName" $name -}} + + {{/* Call class to create the object */}} + {{- include "ix.v1.common.class.secret" (dict "rootCtx" $ "objectData" $objectData) -}} + + {{- end -}} + + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/spawner/_service.tpl b/library/common/1.0.0/templates/spawner/_service.tpl new file mode 100644 index 0000000000..c97fc25901 --- /dev/null +++ b/library/common/1.0.0/templates/spawner/_service.tpl @@ -0,0 +1,39 @@ +{{/* Service Spawner */}} +{{/* Call this template: +{{ include "ix.v1.common.spawner.service" $ -}} +*/}} + +{{- define "ix.v1.common.spawner.service" -}} + + {{/* Primary validation for enabled service. */}} + {{- include "ix.v1.common.lib.service.primaryValidation" $ -}} + + {{- range $name, $service := .Values.service -}} + + {{- if $service.enabled -}} + + {{/* Create a copy of the configmap */}} + {{- $objectData := (mustDeepCopy $service) -}} + + {{- $objectName := include "ix.v1.common.lib.chart.names.fullname" $ -}} + {{- if not $objectData.primary -}} + {{- $objectName = (printf "%s-%s" (include "ix.v1.common.lib.chart.names.fullname" $) $name) -}} + {{- end -}} + + {{/* Perform validations */}} + {{- include "ix.v1.common.lib.chart.names.validation" (dict "name" $objectName) -}} + {{- include "ix.v1.common.lib.metadata.validation" (dict "objectData" $objectData "caller" "Service") -}} + {{- include "ix.v1.common.lib.service.validation" (dict "rootCtx" $ "objectData" $objectData) -}} + + {{/* Set the name of the service account */}} + {{- $_ := set $objectData "name" $objectName -}} + {{- $_ := set $objectData "shortName" $name -}} + + {{/* Call class to create the object */}} + {{- include "ix.v1.common.class.service" (dict "rootCtx" $ "objectData" $objectData) -}} + + {{- end -}} + + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/spawner/_serviceAccount.tpl b/library/common/1.0.0/templates/spawner/_serviceAccount.tpl new file mode 100644 index 0000000000..4851fefd8d --- /dev/null +++ b/library/common/1.0.0/templates/spawner/_serviceAccount.tpl @@ -0,0 +1,38 @@ +{{/* Service Account Spawner */}} +{{/* Call this template: +{{ include "ix.v1.common.spawner.serviceAccount" $ -}} +*/}} + +{{- define "ix.v1.common.spawner.serviceAccount" -}} + + {{/* Primary validation for enabled service accounts. */}} + {{- include "ix.v1.common.lib.serviceAccount.primaryValidation" $ -}} + + {{- range $name, $serviceAccount := .Values.serviceAccount -}} + + {{- if $serviceAccount.enabled -}} + + {{/* Create a copy of the configmap */}} + {{- $objectData := (mustDeepCopy $serviceAccount) -}} + + {{- $objectName := include "ix.v1.common.lib.chart.names.fullname" $ -}} + {{- if not $objectData.primary -}} + {{- $objectName = (printf "%s-%s" (include "ix.v1.common.lib.chart.names.fullname" $) $name) -}} + {{- end -}} + + {{/* Perform validations */}} + {{- include "ix.v1.common.lib.chart.names.validation" (dict "name" $objectName) -}} + {{- include "ix.v1.common.lib.metadata.validation" (dict "objectData" $objectData "caller" "Service Account") -}} + + {{/* Set the name of the service account */}} + {{- $_ := set $objectData "name" $objectName -}} + {{- $_ := set $objectData "shortName" $name -}} + + {{/* Call class to create the object */}} + {{- include "ix.v1.common.class.serviceAccount" (dict "rootCtx" $ "objectData" $objectData) -}} + + {{- end -}} + + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/spawner/_workload.tpl b/library/common/1.0.0/templates/spawner/_workload.tpl new file mode 100644 index 0000000000..69a661656f --- /dev/null +++ b/library/common/1.0.0/templates/spawner/_workload.tpl @@ -0,0 +1,52 @@ +{{/* Workload Spawner */}} +{{/* Call this template: +{{ include "ix.v1.common.spawner.workload" $ -}} +*/}} + +{{- define "ix.v1.common.spawner.workload" -}} + + {{/* Primary validation for enabled workload. */}} + {{- include "ix.v1.common.lib.workload.primaryValidation" $ -}} + + {{- range $name, $workload := .Values.workload -}} + + {{- if $workload.enabled -}} + + {{/* Create a copy of the workload */}} + {{- $objectData := (mustDeepCopy $workload) -}} + + {{/* Generate the name of the workload */}} + {{- $objectName := include "ix.v1.common.lib.chart.names.fullname" $ -}} + {{- if not $objectData.primary -}} + {{- $objectName = printf "%s-%s" (include "ix.v1.common.lib.chart.names.fullname" $) $name -}} + {{- end -}} + + {{/* Perform validations */}} + {{- include "ix.v1.common.lib.chart.names.validation" (dict "name" $objectName) -}} + {{- include "ix.v1.common.lib.metadata.validation" (dict "objectData" $objectData "caller" "Workload") -}} + + {{/* Set the name of the workload */}} + {{- $_ := set $objectData "name" $objectName -}} + + {{/* Short name is the one that defined on the chart, used on selectors */}} + {{- $_ := set $objectData "shortName" $name -}} + + {{/* Set the podSpec so it doesn't fail on nil pointer */}} + {{- if not (hasKey $objectData "podSpec") -}} + {{- fail "Workload - Expected key to exist" -}} + {{- end -}} + + {{/* Call class to create the object */}} + {{- if eq $objectData.type "Deployment" -}} + {{- include "ix.v1.common.class.deployment" (dict "rootCtx" $ "objectData" $objectData) -}} + {{- else if eq $objectData.type "Job" -}} + {{- include "ix.v1.common.class.job" (dict "rootCtx" $ "objectData" $objectData) -}} + {{- else if eq $objectData.type "CronJob" -}} + {{- include "ix.v1.common.class.cronjob" (dict "rootCtx" $ "objectData" $objectData) -}} + {{- end -}} + + {{- end -}} + + {{- end -}} + +{{- end -}} diff --git a/library/common/1.0.0/templates/values/_init.tpl b/library/common/1.0.0/templates/values/_init.tpl new file mode 100644 index 0000000000..69e207602f --- /dev/null +++ b/library/common/1.0.0/templates/values/_init.tpl @@ -0,0 +1,14 @@ +{{/* Merge chart values and the common chart defaults */}} +{{/* The ".common" is the name of the library */}} +{{/* Call this template: +{{ include "ix.v1.common.values.init" $ }} +*/}} + +{{- define "ix.v1.common.values.init" -}} + {{- if .Values.common -}} + {{- $commonValues := mustDeepCopy .Values.common -}} + {{- $chartValues := mustDeepCopy (omit .Values "common") -}} + {{- $mergedValues := mustMergeOverwrite $commonValues $chartValues -}} + {{- $_ := set . "Values" (mustDeepCopy $mergedValues) -}} + {{- end -}} +{{- end -}} diff --git a/library/common/1.0.0/values.yaml b/library/common/1.0.0/values.yaml new file mode 100644 index 0000000000..3581b1986c --- /dev/null +++ b/library/common/1.0.0/values.yaml @@ -0,0 +1,173 @@ +# -- (docs/README.md) +global: + labels: {} + annotations: {} + minNodePort: 9000 + +# -- (docs/README.md) +fallbackDefaults: + probeType: http + serviceProtocol: tcp + serviceType: ClusterIP + persistenceType: emptyDir # TODO: Maybe something else? + probeTimeouts: + liveness: + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 5 + successThreshold: 1 + readiness: + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 5 + successThreshold: 2 + startup: + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 2 + failureThreshold: 60 + successThreshold: 1 + +# -- (docs/README.md) +image: + repository: "" + tag: "" + pullPolicy: IfNotPresent + +# -- (docs/README.md) +securityContext: + container: + runAsNonRoot: true + runAsUser: 568 + runAsGroup: 568 + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + privileged: false + seccompProfile: + type: RuntimeDefault + capabilities: + add: [] + drop: + - ALL + pod: + fsGroup: 568 + fsGroupChangePolicy: OnRootMismatch + supplementalGroups: [] + sysctls: [] + +# -- (docs/README.md) +containerOptions: + PUID: 568 + UMASK: "002" + NVIDIA_CAPS: + - all + resources: + limits: + cpu: 4000m + memory: 8Gi + requests: + cpu: 10m + memory: 50Mi + +# -- (docs/README.md) +podOptions: + enableServiceLinks: false + hostNetwork: false + restartPolicy: Always + dnsPolicy: ClusterFirst + dnsConfig: + options: + - name: ndots + value: "2" + hostAliases: [] + tolerations: [] + runtimeClassName: "" + automountServiceAccountToken: false + terminationGracePeriodSeconds: 120 + +# -- (docs/notes.md) +notes: + header: | + # Welcome to SCALE + Thank you for installing <{{ .Chart.Name }}>. + # custom: "{{ toYaml $.Values }}" + custom: "" + footer: | + # Documentation + Documentation for this chart can be found at ... + # Bug reports + If you find a bug in this chart, please file an issue at ... + +# -- Timezone used everywhere applicable +TZ: UTC + +# -- (docs/workload/README.md) +workload: {} + +# -- (docs/service/README.md) +service: {} + +# -- (docs/persistence/README.md) +persistence: {} + # shared: + # enabled: false # TODO: Enable by default? + # type: emptyDir + # mountPath: /shared + # targetSelectAll: true + # varlogs: + # enabled: false # TODO: Enable by default? + # type: emptyDir + # mountPath: /var/logs + # targetSelectAll: true + # tmp: + # enabled: false # TODO: Enable by default? + # type: emptyDir + # mountPath: /tmp + # targetSelectAll: true + # devshm: + # enabled: false # TODO: Enable by default? + # type: emptyDir + # mountPath: /dev/shm + # targetSelectAll: true + +# -- Injected from SCALE middleware +# Only for reference here +ixExternalInterfacesConfiguration: [] +# -- Injected from SCALE middleware +# Only for reference here +ixExternalInterfacesConfigurationNames: [] +# -- Injected from SCALE middleware +# Only for reference here +ixCertificates: [] +# -- Injected from SCALE middleware +# Only for reference here +ixVolumes: [] + +# -- (docs/imagePullSecrets.md) +imagePullSecret: [] + +# -- (docs/configmap.md) +configmap: {} + +# -- (docs/secret.md) +secret: {} + +# -- (docs/serviceAccount.md) +serviceAccount: {} + +# -- (docs/rbac.md) +rbac: {} + +# -- (docs/scaleExternalInterface.md) +scaleExternalInterface: [] + +# -- (docs/scaleCertificate.md) +scaleCertificate: {} + +# -- (docs/scaleGPU.md) +scaleGPU: [] + +# TODO: +portal: {} diff --git a/run_common_tests.sh b/run_common_tests.sh new file mode 100755 index 0000000000..cf907dc196 --- /dev/null +++ b/run_common_tests.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# https://github.com/quintush/helm-unittest + +# -- You need to install this helm plugin +# helm plugin install https://github.com/quintush/helm-unittest + +common_test_path="library/common-test" + +function cleanup { + if [ -d "$common_test_path/charts" ]; then + echo "🧹 Cleaning up charts..." + rm -r "$common_test_path/charts" + rm "$common_test_path/Chart.lock" + # Clean snapshots + rm -r "$common_test_path/**/__snapshot__" 2> /dev/null + fi +} + +cleanup + +echo "🔨 Building common..." +helm dependency update "$common_test_path" + +echo "🧪 Running tests..." +helm unittest --update-snapshot --helm3 -f "tests/*/*.yaml" "./$common_test_path" + +cleanup