Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 0 additions & 17 deletions charts/tesk/service-info/service-info.yaml

This file was deleted.

133 changes: 133 additions & 0 deletions charts/tesk/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,38 @@ secret) from its environment.
{{- include "tesk.fullname" . }}-service-info
{{- end -}}

{{/*
The ConfigMap with the service info that the API mounts: the operator's
(`serviceInfo.existingConfigMap`, if set, also if YAML reads it as 0 or
false) or the chart's (`tesk.serviceInfoName`, rendered only then).
*/}}
{{- define "tesk.serviceInfoConfigMapName" -}}
{{- $existing := (default dict .Values.serviceInfo).existingConfigMap -}}
{{- if include "tesk.isSet" $existing -}}
{{- toString $existing -}}
{{- else -}}
{{- include "tesk.serviceInfoName" . -}}
{{- end -}}
{{- end -}}

{{/*
The tag of the API's image (tesk-api): `tesk.image.tag`, or the chart's
`appVersion` if it is unset or empty (not `default`, which would replace a
tag of 0). Whole numbers, which Helm reads from values files as floats,
are rendered as written (e.g., 20261110, not 2.026111e+07).
*/}}
{{- define "tesk.apiImageTag" -}}
{{- $tag := dig "image" "tag" "" .Values.tesk -}}
{{- if or (kindIs "invalid" $tag) (eq (toString $tag) "") -}}
{{- $tag = .Chart.AppVersion -}}
{{- end -}}
{{- if and (kindIs "float64" $tag) (eq (floor $tag) $tag) -}}
{{- printf "%.0f" $tag -}}
{{- else -}}
{{- toString $tag -}}
{{- end -}}
Comment thread
uniqueg marked this conversation as resolved.
Resolved
{{- end -}}

{{- define "tesk.oauthClientName" -}}
{{- include "tesk.fullname" . }}-oauth-client
{{- end -}}
Expand Down Expand Up @@ -226,6 +258,14 @@ The notes that `helm install` and `helm upgrade` print (`NOTES.txt`).
{{- define "tesk.notes" -}}
TESK is deployed as release {{ .Release.Name }} in namespace {{ .Release.Namespace }}.
See https://tesk.readthedocs.io/en/latest/deployment/ to check the installation.
{{- $serviceInfo := default dict .Values.serviceInfo -}}
{{- if and (not (include "tesk.isSet" $serviceInfo.existingConfigMap)) (eq (toString $serviceInfo.id) "uk.ac.ebi.tsc.tesk") }}

NOTE: serviceInfo.id is the chart's default, so GET /service-info describes
no particular deployment. Set serviceInfo.id, serviceInfo.organization,
serviceInfo.contactUrl and serviceInfo.environment for yours; see
https://tesk.readthedocs.io/en/latest/configuration/#service-info-serviceinfo
{{- end }}
{{- /* Any user name, also one that YAML reads as 0 or false */ -}}
{{- $username := .Values.ftp.username -}}
{{- $hasUsername := include "tesk.isSet" $username -}}
Expand Down Expand Up @@ -299,6 +339,99 @@ timeout, as before. Use images newer than v0.10.6.
{{- end }}
{{- end -}}

{{/*
The service info (`serviceInfo`) as the YAML file that tesk-api reads
(`TesServiceInfo`), or nothing with `serviceInfo.existingConfigMap`. Text
fields are quoted (`tesk.yamlText`), so that tesk-api reads them as
text whatever they look like; `createdAt` and `updatedAt` are YAML
timestamps, unquoted, as tesk-api reads them as dates. Empty optional
fields are left out; the fields that the GA4GH service-info specification
requires fail if empty. `type` (TES 1.0.0, which tesk-api implements) and
`version` (the tag of tesk-api's image) are set here.
*/}}
{{- define "tesk.serviceInfo" -}}
{{- $info := default dict .Values.serviceInfo -}}
{{- if not (include "tesk.isSet" $info.existingConfigMap) -}}
Comment thread
uniqueg marked this conversation as resolved.
Resolved
{{- $organization := default dict $info.organization -}}
{{- range $field, $value := dict "id" $info.id "name" $info.name "organization.name" $organization.name "organization.url" $organization.url -}}
{{- if not (include "tesk.isSet" $value) -}}
{{- fail (printf "serviceInfo.%s must be set: the GA4GH service-info specification requires it" $field) -}}
{{- end -}}
{{- end -}}
{{- /* YAML 1.1 timestamps, as SnakeYAML resolves them (two-digit month
and day without a time); the same pattern as `$defs/timestamp` in
values.schema.json, which checks them first */ -}}
{{- $timestamp := "^([0-9]{4}-[0-9]{2}-[0-9]{2}|[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}([Tt]|[ \t]+)[0-9]{1,2}:[0-9]{2}:[0-9]{2}(\\.[0-9]*)?([ \t]*(Z|[-+][0-9]{1,2}(:[0-9]{2})?))?)$" -}}
Comment thread
uniqueg marked this conversation as resolved.
Resolved
{{- range $field := list "createdAt" "updatedAt" -}}
{{- $value := index $info $field -}}
{{- if and (include "tesk.isSet" $value) (not (regexMatch $timestamp (toString $value))) -}}
{{- fail (printf "serviceInfo.%s must be a date (e.g., 2026-11-10) or a date and time (e.g., 2026-11-10T12:00:00Z), not %q" $field (toString $value)) -}}
{{- end -}}
{{- end -}}
id: {{ include "tesk.yamlText" (dict "value" $info.id "key" "serviceInfo.id") }}
name: {{ include "tesk.yamlText" (dict "value" $info.name "key" "serviceInfo.name") }}
type:
group: "org.ga4gh"
artifact: "TES"
version: "1.0.0"
{{- if include "tesk.isSet" $info.description }}
description: {{ include "tesk.yamlText" (dict "value" $info.description "key" "serviceInfo.description") }}
{{- end }}
organization:
name: {{ include "tesk.yamlText" (dict "value" $organization.name "key" "serviceInfo.organization.name") }}
url: {{ include "tesk.yamlText" (dict "value" $organization.url "key" "serviceInfo.organization.url") }}
{{- range $field := list "contactUrl" "documentationUrl" }}
{{- $value := index $info . }}
{{- if include "tesk.isSet" $value }}
{{ $field }}: {{ include "tesk.yamlText" (dict "value" $value "key" (printf "serviceInfo.%s" $field)) }}
{{- end }}
{{- end }}
{{- range $field := list "createdAt" "updatedAt" }}
{{- $value := index $info . }}
{{- if include "tesk.isSet" $value }}
{{ $field }}: {{ toString $value }}
{{- end }}
{{- end }}
{{- if include "tesk.isSet" $info.environment }}
environment: {{ include "tesk.yamlText" (dict "value" $info.environment "key" "serviceInfo.environment") }}
{{- end }}
version: {{ include "tesk.apiImageTag" . | toJson }}
{{- with $info.storage }}
storage:
{{- range . }}
- {{ include "tesk.yamlText" (dict "value" . "key" "serviceInfo.storage") }}
{{- end }}
{{- end }}
{{- end -}}
{{- end -}}

{{/*
A value as a quoted YAML string (a JSON string, which YAML reads as a
double-quoted scalar, with DEL and NEL escaped too, which JSON leaves as
they are and Helm's YAML parser rejects). Other characters that YAML
doesn't allow and JSON doesn't escape (the other C1 controls, U+FFFE and
U+FFFF) fail with the value's key. Whole numbers, which Helm reads from values files as floats,
are rendered as written (e.g., 12345678, not 1.2345678e+07) up to 2^53,
and fail above, where they would be rounded: quote them in the values
file. Decimals lose trailing zeros (1.10 is 1.1).

Takes the value (`value`) and its key (`key`), for the errors.
*/}}
{{- define "tesk.yamlText" -}}
{{- $value := .value -}}
{{- $text := toString $value -}}
{{- if and (kindIs "float64" $value) (eq (floor $value) $value) -}}
{{- if not (and (lt $value 9007199254740992.0) (gt $value -9007199254740992.0)) -}}
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
Resolved
{{- fail (printf "%s is a number too large to be exact: quote it in the values file or use --set-string" .key) -}}
{{- end -}}
{{- $text = printf "%.0f" $value -}}
{{- end -}}
{{- if regexMatch "[\u0080-\u0084\u0086-\u009f\ufffe\uffff]" $text -}}
{{- fail (printf "%s has a control character that YAML doesn't allow (U+0080 to U+009F but U+0085, U+FFFE or U+FFFF)" .key) -}}
{{- end -}}
{{- toJson $text | replace "\u007f" "\\u007f" | replace "\u0085" "\\u0085" -}}
{{- end -}}

{{/*
Whole seconds from a value under `tesk` (`name`), with a `minimum` (0 or 1),
as the taskmaster reads them: empty if unset (null or empty); fails on
Expand Down
11 changes: 8 additions & 3 deletions charts/tesk/templates/common/service-info-cm.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
{{- /* The service info from `serviceInfo`, unless an existing ConfigMap
replaces it (`serviceInfo.existingConfigMap`) */ -}}
{{- with include "tesk.serviceInfo" . }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "tesk.serviceInfoName" . }}
name: {{ include "tesk.serviceInfoName" $ }}
labels:
{{- include "tesk.labels" . | nindent 4 }}
{{- include "tesk.labels" $ | nindent 4 }}
data:
{{ (.Files.Glob "service-info/*").AsConfig | indent 2 }}
service-info.yaml: |
{{- . | nindent 4 }}
Comment thread
uniqueg marked this conversation as resolved.
Resolved
{{- end }}
20 changes: 12 additions & 8 deletions charts/tesk/templates/common/tesk-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@ whole chart */ -}}
{{- include "tesk.checkImageReleases" . -}}
{{- $tesk := .Values.tesk -}}
{{- $repository := dig "image" "repository" "" $tesk | default "docker.io/elixircloud/tesk-api" -}}
{{- $tag := dig "image" "tag" "" $tesk -}}
{{- /* Not `default`, which would replace a tag of 0 */ -}}
{{- if or (kindIs "invalid" $tag) (eq (toString $tag) "") -}}
{{- $tag = .Chart.AppVersion -}}
{{- end -}}
{{- /* Also the version of the service info */ -}}
{{- $tag := include "tesk.apiImageTag" . -}}
{{- $ftpSecretName := include "tesk.ftpSecretName" . -}}
{{- $executorSecurityContext := include "tesk.executorSecurityContext" . -}}
{{- $serviceInfo := include "tesk.serviceInfo" . -}}
apiVersion: apps/v1
kind: Deployment
metadata:
Expand All @@ -28,10 +26,16 @@ spec:
metadata:
labels:
{{- include "tesk.selectorLabels" . | nindent 8 }}
{{- /* tesk-api reads the ConfigMap only at startup */}}
{{- with $executorSecurityContext }}
{{- /* tesk-api reads the executors' ConfigMap only at startup, and
the service info at startup and then every hour */}}
{{- if or $executorSecurityContext $serviceInfo }}
annotations:
{{- with $executorSecurityContext }}
checksum/executor-config: {{ sha256sum . }}
{{- end }}
{{- with $serviceInfo }}
checksum/service-info: {{ sha256sum . }}
{{- end }}
{{- end }}
spec:
serviceAccountName: {{ include "tesk.taskmasterName" . }}
Expand Down Expand Up @@ -209,7 +213,7 @@ spec:
volumes:
- name: service-info
configMap:
name: {{ include "tesk.serviceInfoName" . }}
name: {{ include "tesk.serviceInfoConfigMapName" . | quote }}
{{- with $tesk.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
Expand Down
85 changes: 85 additions & 0 deletions charts/tesk/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@
},
"switch": {
"type": "boolean"
},
"timestamp": {
"type": [
"string",
"null"
],
"pattern": "^([0-9]{4}-[0-9]{2}-[0-9]{2}|[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}([Tt]|[ \\t]+)[0-9]{1,2}:[0-9]{2}:[0-9]{2}(\\.[0-9]*)?([ \\t]*(Z|[-+][0-9]{1,2}(:[0-9]{2})?))?)?$",
"$comment": "A YAML 1.1 timestamp, as tesk-api's SnakeYAML resolves it (two-digit month and day without a time), or empty; the same pattern as in tesk.serviceInfo (templates/_helpers.tpl)"
}
},
"properties": {
Expand Down Expand Up @@ -461,6 +469,83 @@
},
"additionalProperties": false
},
"serviceInfo": {
"type": [
"object",
"null"
],
"description": "Service info, the answer of GET /service-info (GA4GH service-info specification); type and version are set by the chart",
"additionalProperties": false,
"properties": {
"existingConfigMap": {
"type": [
"string",
"null"
],
"description": "Name of a ConfigMap of your own with the key service-info.yaml, instead of the chart's; the other fields are then ignored"
},
"id": {
"$ref": "#/$defs/scalar",
"description": "Unique ID of the deployment, in reverse domain notation (required)"
},
"name": {
"$ref": "#/$defs/scalar",
"description": "Name of the service (required)"
},
"description": {
"$ref": "#/$defs/scalar",
"description": "Description of the service"
},
"organization": {
"type": [
"object",
"null"
],
"description": "The organisation that runs the service",
"additionalProperties": false,
"properties": {
"name": {
"$ref": "#/$defs/scalar",
"description": "Name of the organisation (required)"
},
"url": {
"$ref": "#/$defs/scalar",
"description": "URL of the organisation (required)"
}
}
},
"contactUrl": {
"$ref": "#/$defs/scalar",
"description": "Where users get support, e.g., a mailto: URL"
},
"documentationUrl": {
"$ref": "#/$defs/scalar",
"description": "Documentation for users of the service"
},
"createdAt": {
"$ref": "#/$defs/timestamp",
"description": "When the service was created: a date (2026-11-10) or a date and time (2026-11-10T12:00:00Z)"
},
"updatedAt": {
"$ref": "#/$defs/timestamp",
"description": "When the service was last updated: a date or a date and time"
},
"environment": {
"$ref": "#/$defs/scalar",
"description": "Environment of the service, e.g., test or prod"
},
"storage": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "Storage locations that tasks can use, e.g., s3://bucket"
}
}
},
"service": {
"type": "object",
"properties": {
Expand Down
36 changes: 36 additions & 0 deletions charts/tesk/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,42 @@ auth:
# client, which needs no secret: `clientSecret` is deprecated, and
# ignored by tesk-api 1.2.0 and later; leave it unset

# Service info, the answer of GET /service-info (GA4GH service-info
# specification), rendered into a ConfigMap. The defaults describe no
# particular deployment: set at least `id`, `organization`, `contactUrl` and
# `environment` for yours. The chart sets `type` (TES 1.0.0) and `version`
# (the tag of tesk-api's image) itself. Empty optional fields are left out
serviceInfo:
# Name of a ConfigMap of your own with the key `service-info.yaml`, in the
# release's namespace, instead of the chart's; the other fields are then
# ignored
existingConfigMap: ""
# Unique ID of this deployment, in reverse domain notation, e.g.,
# org.example.tes (required). The notes remind you while it is this
# default (`tesk.notes` in templates/_helpers.tpl). Quote text that YAML
# would read as a number (e.g., "1.10")
id: uk.ac.ebi.tsc.tesk
# Name of the service (required)
name: TESK
description: GA4GH TES Server implementation for Kubernetes
# The organisation that runs the service (both required)
organization:
name: EMBL-EBI
url: https://www.ebi.ac.uk/
# Where users get support (a URL, e.g., mailto:tes@example.org)
contactUrl: https://github.com/elixir-cloud-aai/TESK/issues
# Documentation for users of the service
documentationUrl: https://tesk.readthedocs.io/
# When the service was created and last updated: a date with two-digit
# month and day (2026-11-10) or a date and time (2026-11-10T12:00:00Z)
createdAt: "2021-04-06"
updatedAt: ""
# e.g., test, staging or prod
environment: dev
# Storage locations that tasks can use, e.g., [s3://bucket]
storage:
- ftp://ftp-private.ebi.ac.uk

service:
# Type of the API's Service: `NodePort` to expose the API directly,
# `LoadBalancer` on clusters of cloud providers, or empty for `ClusterIP`
Expand Down
Loading
Loading