initial service wireframe

This commit is contained in:
Stavros kois
2023-02-04 15:03:39 +02:00
parent 99d959e571
commit 2ab542a776
12 changed files with 667 additions and 10 deletions
@@ -0,0 +1,97 @@
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
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
g_label1: global_label1
g_label2: global_label2
label1: label1
label2: label2
- it: should pass with service type LoadBalancer, with HTTPS port and addMetalLBAnnotations/Traefik true
set:
global:
addMetalLBAnnotations: true
addTraefikAnnotations: true
service:
my-service1:
enabled: true
primary: true
type: LoadBalancer
ports:
port-name:
enabled: true
primary: true
protocol: HTTPS
workload:
my-workload:
enabled: true
primary: true
type: Deployment
podSpec: {}
asserts:
- documentIndex: *serviceDoc
isKind:
of: Service
- documentIndex: *serviceDoc
isSubset:
path: metadata.annotations
content:
metallb.universe.tf/allow-shared-ip: release-name-common-test
traefik.ingress.kubernetes.io/service.serversscheme: https
@@ -0,0 +1,47 @@
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
my-service2:
enabled: true
ports:
port-name:
enabled: true
primary: true
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
@@ -0,0 +1,191 @@
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 <annotations> 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 <labels> 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 service's <targetSelector> 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 port's <targetSelector> 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
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
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
protocol: not-a-protocol
asserts:
- failedTemplate:
errorMessage: Service - Expected port's protocol to be one of [TCP, UDP, HTTP, HTTPS] but got [not-a-protocol]
+6 -2
View File
@@ -1,7 +1,11 @@
# As we have by default enabled
# a "main" workload" for all charts
# a "main" workload and service" for all charts
# We disable it so tests run with their defined
# workload (Tests use a different named workload)
# values (Tests use a different named values)
workload:
main:
enabled: false
service:
main:
enabled: false
@@ -0,0 +1,70 @@
{{/* Service Class */}}
{{/* Call this template:
{{ include "ix.v1.common.class.service" (dict "rootCtx" $ "objectData" $objectData) }}
rootCtx: The root context of the template. It is used to access the global context.
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 "ClusterIP" -}}
{{/* Get Pod Values based on the selector (or the absence of it) */}}
{{- $podValues := fromJson (include "ix.v1.common.lib.service.getSelectedPodValues" (dict "rootCtx" $rootCtx "objectData" $objectData)) -}}
{{/* Get Pod's hostNetwork configuration */}}
{{- $hostNetwork := include "ix.v1.common.lib.pod.hostNetwork" (dict "rootCtx" $rootCtx "objectData" $podValues) -}}
{{- $hasHTTPSPort := false -}}
{{- $hasHostPort := false -}}
{{- range $portName, $port := $objectData.ports -}}
{{- if $port.enabled -}}
{{- if eq ($port.protocol | default "") "HTTPS" -}}
{{- $hasHTTPSPort = true -}}
{{- end -}}
{{- if and (hasKey $port "hostPort") $port.hostPort -}}
{{- $hasHostPort = true -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/* When hostNetwork is set on the pod, force ClusterIP, so services won't 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)) -}}
{{- 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)) -}}
{{- if eq $svcType "LoadBalancer" -}}
{{- include "ix.v1.common.lib.service.metalLBAnnotations" (dict "rootCtx" $rootCtx "annotations" $annotations) -}}
{{- end -}}
{{- if and $hasHTTPSPort -}}
{{- include "ix.v1.common.lib.service.traefikAnnotations" (dict "rootCtx" $rootCtx "annotations" $annotations) -}}
{{- end -}}
{{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "annotations" $annotations) | trim) }}
annotations:
{{- . | nindent 4 }}
{{- end }}
spec:
{{/* TODO: */}}
{{- end -}}
@@ -1,6 +1,7 @@
{{/* 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 -}}
@@ -0,0 +1,29 @@
{{/* Service - MetalLB Annotations */}}
{{/* Call this template:
{{ include "ix.v1.common.lib.service.metalLBAnnotations" (dict "rootCtx" $rootCtx "annotations" $annotations) -}}
rootCtx: The root context of the service
*/}}
{{- define "ix.v1.common.lib.service.metalLBAnnotations" -}}
{{- $rootCtx := .rootCtx -}}
{{- $annotations := .annotations -}}
{{- if $rootCtx.Values.global.addMetalLBAnnotations -}}
{{- $_ := set $annotations "metallb.universe.tf/allow-shared-ip" (include "ix.v1.common.lib.chart.names.fullname" $rootCtx) -}}
{{- end -}}
{{- end -}}
{{/* Service - Traefik Annotations */}}
{{/* Call this template:
{{ include "ix.v1.common.lib.service.traefikAnnotations" (dict "rootCtx" $rootCtx "annotations" $annotations) -}}
rootCtx: The root context of the service
*/}}
{{- define "ix.v1.common.lib.service.traefikAnnotations" -}}
{{- $rootCtx := .rootCtx -}}
{{- $annotations := .annotations -}}
{{- if $rootCtx.Values.global.addTraefikAnnotations -}}
{{- $_ := set $annotations "traefik.ingress.kubernetes.io/service.serversscheme" "https" -}}
{{- end -}}
{{- end -}}
@@ -0,0 +1,46 @@
{{/* Service - Get Selected Pod */}}
{{/* Call this template:
{{ include "ix.v1.common.lib.service.getSelectedPodValues" (dict "rootCtx" $rootCtx "objectData" $objectData) -}}
objectData: The object data of the service
rootCtx: The root context of the service
*/}}
{{- define "ix.v1.common.lib.service.getSelectedPodValues" -}}
{{- $rootCtx := .rootCtx -}}
{{- $objectData := .objectData -}}
{{- $podValues := dict -}}
{{- with $objectData.targetSelector -}}
{{- $podValues = mustDeepCopy (get $rootCtx.Values.workload .) -}}
{{- if not $podValues -}}
{{- fail (printf "Service - Selected pod [%s] is not defined" .) -}}
{{- end -}}
{{- if not $podValues.enabled -}}
{{- fail (printf "Service - Selected pod [%s] is not enabled" .) -}}
{{- 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 -}}
@@ -0,0 +1,126 @@
{{/* Service Validation */}}
{{/* Call this template:
{{ include "ix.v1.common.lib.service.validation" (dict "objectData" $objectData) -}}
objectData:
labels: The labels of the service.
annotations: The annotations of the service.
*/}}
{{- define "ix.v1.common.lib.service.validation" -}}
{{- $objectData := .objectData -}}
{{- if and $objectData.labels (not (kindIs "map" $objectData.labels)) -}}
{{- fail (printf "Service - Expected <labels> to be a dictionary, but got [%v]" (kindOf $objectData.labels)) -}}
{{- end -}}
{{- if and $objectData.annotations (not (kindIs "map" $objectData.annotations)) -}}
{{- fail (printf "Service - Expected <annotations> to be a dictionary, but got [%v]" (kindOf $objectData.annotations)) -}}
{{- end -}}
{{- if and $objectData.targetSelector (not (kindIs "string" $objectData.targetSelector)) -}}
{{- fail (printf "Service - Expected service's <targetSelector> to be [string], but got [%s]" (kindOf $objectData.targetSelector)) -}}
{{- 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 port's <targetSelector> to be [string], but got [%s]" (kindOf $port.targetSelector)) -}}
{{- end -}}
{{- if and $port.protocol (not (mustHas $port.protocol (list "TCP" "UDP" "HTTP" "HTTPS"))) -}}
{{- fail (printf "Service - Expected port's protocol to be one of [TCP, UDP, HTTP, HTTPS] but got [%s]" $port.protocol) -}}
{{- 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 -}}
@@ -19,4 +19,7 @@
{{/* Render Workload(s) */}}
{{- include "ix.v1.common.spawner.workload" . | nindent 0 -}}
{{/* Render Services(s) */}}
{{- include "ix.v1.common.spawner.service" . | nindent 0 -}}
{{- end -}}
@@ -0,0 +1,38 @@
{{/* 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.service.validation" (dict "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 -}}
+13 -8
View File
@@ -4,11 +4,12 @@ global:
labels: {}
# -- Set additional global annotations
annotations: {}
# -- Adds metalLB annotations to services
addMetalLBAnnotations: false
# -- Adds traefik annotations to services
addTraefikAnnotations: false
# -- Scale Storage Class
scaleZFSStorageClass: '{{ printf "ix-storage-class-%v" .Release.Name }}'
ixChartContext:
addNvidiaRuntimeClass: true
nvidiaRuntimeClassName: asdf
# TODO:
fallbackDefaults:
@@ -251,16 +252,20 @@ persistence:
subPath: ""
mountPropagation: ""
# -- Services
services:
# -- Service
service:
# -- Service name
service-name:
main:
# -- Enables the service
enabled: true
enabled: false
# -- Primary service
primary: true
# -- Service labels
labels: {}
# -- Service annotations
annotations: {}
# -- Pod to connect
targetSelector: pod-name
targetSelector: main
# -- Ports
ports:
port-name: