diff --git a/.github/workflows/_helm.yml b/.github/workflows/_helm.yml new file mode 100644 index 000000000..33055ccc1 --- /dev/null +++ b/.github/workflows/_helm.yml @@ -0,0 +1,71 @@ +name: Package helm charts + +on: + workflow_call: + +env: + HELM_VERSION_TO_INSTALL: 3.14.3 + +jobs: + package-helm-charts: + name: Package and Push Helm Chart + runs-on: ubuntu-latest + environment: release + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install helm + uses: Azure/setup-helm@v3 + with: + version: ${{ env.HELM_VERSION_TO_INSTALL }} + + # Check that alpha/beta versions have the form X.Y.Z-alpha.A requried by Helm. + # An early check saves waiting for the entire build before finding a problem. + - name: Check helm version tag + if: ${{ github.ref_type == 'tag' }} + env: + VERSION: "${{ github.ref_name }}" + run: | + if [[ "${VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-alpha|-beta|-rc).*?$ ]]; then + echo "Valid version format: ${VERSION}" + else + echo "Invalid version: ${VERSION}. Expected: X.Y.Z or X.Y.Z-beta.1 or X.Y.Z-alpha.1" + exit 1 + fi + + - name: Package helm charts + env: + VERSION: "${{ github.ref_type == 'tag' && github.ref_name || '0.0.0' }}" + run: | + set -xe + + mkdir -p charts + for i in Charts/*; do + if [[ ${i} =~ ^.*-ioc$ ]]; then + echo "Skipping IOC schema chart: ${i}" + continue + fi + echo "Packaging chart: ${i}" + helm package -u --app-version ${VERSION} --version ${VERSION} ${i} + mv $(basename ${i})-*.tgz charts/ + done + + - name: Upload helm chart values schemas + uses: actions/upload-artifact@v4 + with: + name: helm-chart-schemas + path: schemas/ + + - name: Push tagged helm chart to registry + if: ${{ github.ref_type == 'tag' }} + run: | + set -x + + echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ghcr.io/${{ github.repository_owner }} --username ${{ github.repository_owner }} --password-stdin + REGISTRY=oci://ghcr.io/diamondlightsource/charts + for i in charts/*.tgz; do + helm push "${i}" ${REGISTRY} + done diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9529f09e4..6f661fd49 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,6 +44,12 @@ jobs: if: needs.check.outputs.branch-pr == '' uses: ./.github/workflows/_dist.yml + helm: + uses: ./.github/workflows/_helm.yml + permissions: + contents: read + packages: write + pypi: if: github.ref_type == 'tag' needs: dist @@ -53,7 +59,7 @@ jobs: release: if: github.ref_type == 'tag' - needs: [dist, docs] + needs: [dist, docs, helm] uses: ./.github/workflows/_release.yml permissions: contents: write diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 60fc23f9a..deebb103e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,9 +1,10 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v5.0.0 hooks: - id: check-added-large-files - id: check-yaml + exclude: ^Charts/ - id: check-merge-conflict - id: end-of-file-fixer @@ -22,3 +23,10 @@ repos: entry: ruff format --force-exclude types: [python] require_serial: true + + - id: helm-schema + name: Generate Helm schema + entry: ./generate-schemas.sh + language: system + types: [yaml] + require_serial: true diff --git a/Charts/fastcs-ioc/Chart.yaml b/Charts/fastcs-ioc/Chart.yaml new file mode 100644 index 000000000..36075b285 --- /dev/null +++ b/Charts/fastcs-ioc/Chart.yaml @@ -0,0 +1,8 @@ +apiVersion: v2 +name: fastcs-instance-ioc +version: 0.1.0 +# This chart allows generating a schema for the fastcs-instance-ioc values file. +# fastcs-instance-ioc schema is used by IOC instances which have fastcs-instance +# as a dependency. +# +# This chart is never used except by helm-schema. diff --git a/Charts/fastcs-ioc/values.schema.json b/Charts/fastcs-ioc/values.schema.json new file mode 100644 index 000000000..e8c342173 --- /dev/null +++ b/Charts/fastcs-ioc/values.schema.json @@ -0,0 +1,249 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "fastcs": { + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "affinity": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.Affinity", + "description": "Affinity for the pod", + "required": [], + "title": "affinity", + "type": "object" + }, + "args": { + "description": "arguments to pass to the above command", + "items": { + "required": [], + "type": "string" + }, + "required": [], + "title": "args", + "type": "array" + }, + "baseIp": { + "default": "10.96.0.0/12", + "description": "Used by allocateIpFromName to allocate a fixed cluster IP for the service.\nThe default is the same for all DLS clusters.\n", + "pattern": "^(\\d{1,3}\\.){3}\\d{1,3}\\/\\d{1,2}$", + "required": [], + "title": "CIDR for services addresses.", + "type": "string" + }, + "ca_server_port": { + "default": 5064, + "description": "service port for Channel Access", + "required": [], + "title": "ca_server_port", + "type": "integer" + }, + "clusterIP": { + "default": "", + "format": "ipv4", + "required": [], + "title": "Override for the cluster IP - only needed if allocateIpFromName clashes", + "type": [ + "string", + "null" + ] + }, + "command": { + "description": "Command to run in the container (as array of arguments)", + "items": { + "required": [], + "type": "string" + }, + "required": [], + "title": "command", + "type": "array" + }, + "extra_containers": { + "description": "adds addtional containers specified by image and command", + "items": { + "additionalProperties": false, + "properties": { + "command": { + "description": "Command to run in the container (as array of arguments)", + "required": [], + "type": "array" + }, + "image": { + "description": "Container image URI", + "format": "image", + "required": [], + "type": "string" + }, + "name": { + "description": "A name for the additional container", + "required": [], + "type": "string" + } + }, + "required": [ + "name", + "image", + "command" + ], + "type": "object" + }, + "required": [], + "title": "extra_containers", + "type": "array" + }, + "global": { + "description": "Global values are values that can be accessed from any chart or subchart by exactly the same name.", + "required": [], + "title": "global", + "type": "object" + }, + "image": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.Container/properties/image", + "required": [], + "title": "image", + "type": "string" + }, + "iocConfig": { + "default": "/epics/ioc/config", + "description": "location of config folder (defaults to be the same as C++ IOCs)", + "required": [], + "title": "iocConfig", + "type": "string" + }, + "livenessProbe": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.Probe", + "required": [], + "title": "livenessProbe", + "type": "object" + }, + "nodeSelector": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.NodeSelector", + "description": "Node selector for the pod", + "required": [], + "title": "nodeSelector", + "type": [ + "object", + "null" + ] + }, + "podAnnotations": { + "additionalProperties": false, + "required": [], + "title": "Pod Annotations", + "type": "object" + }, + "podLabels": { + "additionalProperties": false, + "required": [], + "title": "Pod Labels", + "type": "object" + }, + "podSecurityContext": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.SecurityContext", + "required": [], + "title": "Pod Security Context", + "type": "object" + }, + "pva_server_port": { + "default": 5075, + "description": "service port for PV Access", + "required": [], + "title": "pva_server_port", + "type": "integer" + }, + "readinessProbe": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.Probe", + "required": [], + "title": "readinessProbe", + "type": "object" + }, + "resources": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.ResourceRequirements", + "required": [], + "title": "Resource limits and requests", + "type": "object" + }, + "securityContext": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.SecurityContext", + "required": [], + "title": "Container Security Context", + "type": "object" + }, + "service": { + "additionalProperties": false, + "description": "The service will be configured for Channel Access and PVA. Here you can override\nthe ports and also make this a LoadBalancer service if required.\n", + "properties": { + "ca_port": { + "default": 5064, + "required": [], + "title": "ca_port", + "type": "integer" + }, + "pva_port": { + "default": 5075, + "required": [], + "title": "pva_port", + "type": "integer" + }, + "type": { + "default": "ClusterIP", + "required": [], + "title": "type", + "type": "string" + } + }, + "required": [ + "type", + "ca_port", + "pva_port" + ], + "title": "service", + "type": "object" + }, + "tolerations": { + "description": "Tolerations for the pod", + "items": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.Toleration", + "required": [], + "type": "object" + }, + "required": [], + "title": "tolerations", + "type": "array" + }, + "volumeMounts": { + "items": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.VolumeMount", + "required": [], + "type": "object" + }, + "required": [], + "title": "volumeMounts", + "type": "array" + }, + "volumes": { + "description": "Additional volumes to mount in the output Deployment definition.", + "items": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.Volume", + "required": [], + "type": "object" + }, + "required": [], + "title": "volumes", + "type": "array" + } + }, + "required": [], + "title": "fastcs", + "type": "object" + }, + "global": { + "description": "Global values are values that can be accessed from any chart or subchart by exactly the same name.", + "required": [], + "title": "global", + "type": "object" + } + }, + "required": [], + "type": "object" +} diff --git a/Charts/fastcs-ioc/values.yaml b/Charts/fastcs-ioc/values.yaml new file mode 100644 index 000000000..5a346bb65 --- /dev/null +++ b/Charts/fastcs-ioc/values.yaml @@ -0,0 +1,9 @@ +# yaml-language-server: $schema=values.schema.json + +# @schema +# title: FastCS IOC Values +# description: Values for the FastCS IOC instance +# type: object +# $ref: ../fastcs/values.schema.json +# @schema +fastcs: {} diff --git a/Charts/fastcs/.helmignore b/Charts/fastcs/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/Charts/fastcs/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/Charts/fastcs/Chart.yaml b/Charts/fastcs/Chart.yaml new file mode 100644 index 000000000..2ad3ed918 --- /dev/null +++ b/Charts/fastcs/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: fastcs-instance +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/Charts/fastcs/templates/_helpers.tpl b/Charts/fastcs/templates/_helpers.tpl new file mode 100644 index 000000000..3a598e647 --- /dev/null +++ b/Charts/fastcs/templates/_helpers.tpl @@ -0,0 +1,51 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "fastcs.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "fastcs.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "fastcs.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "fastcs.labels" -}} +helm.sh/chart: {{ include "fastcs.chart" . }} +{{ include "fastcs.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "fastcs.selectorLabels" -}} +app.kubernetes.io/name: {{ include "fastcs.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} diff --git a/Charts/fastcs/templates/clusterIP.tpl b/Charts/fastcs/templates/clusterIP.tpl new file mode 100644 index 000000000..e1b43bec9 --- /dev/null +++ b/Charts/fastcs/templates/clusterIP.tpl @@ -0,0 +1,73 @@ + +{{- define "allocateIpFromName" -}} + {{- $name := printf "%s.%s" .name .namespace -}} + {{- $baseIpWithCIDR := .baseIp -}} + + {{- $startIp := .startIp | int -}} + {{- $conversion := atoi (adler32sum $name) -}} + + {{- $baseIpParts := split "/" $baseIpWithCIDR -}} + {{- $baseIp := index $baseIpParts "_0" -}} + {{- $cidrRange := index $baseIpParts "_1" | int -}} + + {{- $octets := split "." $baseIp -}} + {{- $firstOctet := index $octets "_0" | int -}} + {{- $secondOctet := index $octets "_1" | int -}} + {{- $thirdOctet := index $octets "_2" | int -}} + {{- $fourthOctet := index $octets "_3" | int -}} + + + {{- $totalIps := 1 }} + {{- $loopcnt:= sub 32 $cidrRange -}} + {{- range $i,$k := until ($loopcnt | int) }} + {{- $totalIps = mul $totalIps 2 }} + {{- end }} + + {{- $ipSuffix := add $startIp (mod $conversion $totalIps) -}} + + {{- $secondOctet := add $secondOctet (div $ipSuffix 65536) -}} + {{- $ipSuffix = mod $ipSuffix 65536 -}} + {{- $thirdOctet := add $thirdOctet (div $ipSuffix 256) -}} + {{- $fourthOctet := mod $ipSuffix 256 -}} + + {{- if gt $fourthOctet 255 }} + {{- $fourthOctet = mod $fourthOctet 256 -}} + {{- end }} + {{- if gt $thirdOctet 255 }} + {{- $thirdOctet = mod $thirdOctet 256 -}} + {{- $secondOctet = add $secondOctet 1 -}} + {{- end }} + {{- if gt $secondOctet 255 }} + {{- $secondOctet = mod $secondOctet 256 -}} + {{- end }} + + {{- printf "%d.%d.%d.%d" $firstOctet $secondOctet $thirdOctet $fourthOctet -}} +{{- end -}} + + +{{- define "allocateIpFromNames" -}} + {{- $name := printf "%s.%s" .name .namespace -}} + {{- $baseIpWithCIDR := .baseIp -}} + + {{- $startIp := .startIp | int -}} + {{- $conversion := atoi (adler32sum $name) -}} + + {{- $baseIpParts := split "/" $baseIpWithCIDR -}} + {{- $baseIp := index $baseIpParts "_0" -}} + {{- $cidrRange := index $baseIpParts "_1" | int -}} + + {{- $octets := split "." $baseIp -}} + {{- $firstOctet := index $octets "_0" | int -}} + {{- $secondOctet := index $octets "_1" | int -}} + {{- $thirdOctet := index $octets "_2" | int -}} + {{- $fourthOctet := index $octets "_3" | int -}} + + {{- $totalIps := 1 }} + {{- $loopcnt:= sub 32 $cidrRange -}} + {{- range $i,$k := until ($loopcnt | int) }} + {{- $totalIps = mul $totalIps 2 }} + {{- end }} + {{- printf "CIDR %d IPs %d" $cidrRange $totalIps -}} + + +{{- end -}} diff --git a/Charts/fastcs/templates/deployment.yaml b/Charts/fastcs/templates/deployment.yaml new file mode 100644 index 000000000..9b1705cc3 --- /dev/null +++ b/Charts/fastcs/templates/deployment.yaml @@ -0,0 +1,153 @@ +{{- /* +Default the derivable substitution values. + +This keeps the length of the values.txt file for each individual IOC +to a minimum +*/ -}} +{{- $location := default .Values.global.location .Values.location | required "ERROR - You must supply location or global.location" -}} +{{- $ioc_group := default .Values.global.ioc_group .Values.ioc_group | required "ERROR - You must supply ioc_group or global.ioc_group" -}} +{{- $opisClaim := default (print $ioc_group "-opi-claim") .Values.opisClaim -}} +{{- $runtimeClaim := default (print $ioc_group "-runtime-claim") .Values.runtimeClaim -}} +{{- $autosaveClaim := default (print $ioc_group "-autosave-claim") .Values.autosaveClaim -}} +{{- $image := .Values.image | required "ERROR - You must supply image." -}} + +{{- $enabled := eq .Values.global.enabled false | ternary false true -}} + +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ .Release.Name }} + labels: + location: {{ $location }} + ioc_group: {{ $ioc_group }} + enabled: {{ $enabled | quote }} + is_ioc: "true" + {{- include "fastcs.labels" . | nindent 4 }} +spec: + replicas: {{ $enabled | ternary 1 0 }} + selector: + matchLabels: + {{- include "fastcs.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + location: {{ $location }} + ioc_group: {{ $ioc_group }} + enabled: {{ $enabled | quote }} + is_ioc: "true" + {{- include "fastcs.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + # re-deploy in case the configMap has changed - use a random value + # unless the Commit Hash is supplied (by ArgoCD or helm command line) + rollme: {{ .Values.global.commitHash | default (randAlphaNum 5) | quote }} + spec: + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + {{- range .Values.extra_containers }} + - name: {{ .name }} + {{- with $.Values.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + image: {{ .image }} + {{- with .command }} + command: + {{- if (kindIs "string" .) }} + - {{ . }} + {{- else if (kindIs "slice" .) }} + {{- . | toYaml | nindent 10 }} + {{- end }} + {{- end }} + volumeMounts: + {{- with $.Values.volumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + - name: opis-volume + mountPath: /epics/opi + subPath: "{{ $.Release.Name }}" + - name: config-volume + mountPath: {{ $.Values.iocConfig }} + {{- end }} + - name: {{ .Chart.Name }} + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + image: "{{ .Values.image }}" + imagePullPolicy: IfNotPresent + {{- with .Values.command }} + command: + {{- if (kindIs "string" .) }} + - {{ . }} + {{- else if (kindIs "slice" .) }} + {{- . | toYaml | nindent 10 }} + {{- end }} + {{- end }} + {{- with .Values.args }} + args: + {{- if (kindIs "string" .) }} + - {{ . }} + {{- else if (kindIs "slice" .) }} + {{- . | toYaml | nindent 10 }} + {{- end }} + {{- end }} + + ports: + - name: channel-access + containerPort: 5064 + protocol: TCP + - name: pv-access + containerPort: 5075 + protocol: TCP + {{- with .Values.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + {{- with .Values.volumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + - name: opis-volume + mountPath: /epics/opi + subPath: "{{ .Release.Name }}" + - name: config-volume + mountPath: {{ .Values.iocConfig }} + volumes: + {{- with .Values.volumes }} + {{- toYaml . | nindent 8 }} + {{- end }} + - name: opis-volume + persistentVolumeClaim: + claimName: {{ $opisClaim }} + - name: config-volume + configMap: + name: {{ .Release.Name }}-config + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/Charts/fastcs/templates/service.yaml b/Charts/fastcs/templates/service.yaml new file mode 100644 index 000000000..c51433f8b --- /dev/null +++ b/Charts/fastcs/templates/service.yaml @@ -0,0 +1,52 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }} + labels: + {{- include "fastcs.labels" . | nindent 4 }} + app: {{ .Release.Name }} + location: {{ .Values.global.location }} + ioc_group: {{ .Values.global.ioc_group }} + is_ioc: "true" +spec: + selector: + app: {{ .Release.Name }} + type: {{ .Values.service.type }} + # allocate a fixed clusterIP for this service based on the service name + {{- $alloc_args := dict "name" .Release.Name "namespace" .Release.Namespace "baseIp" .Values.baseIp "startIp" .Values.startIp }} + clusterIP: {{ .Values.clusterIP | default (include "allocateIpFromName" $alloc_args) }} + ports: + - name: ca-server-tcp + port: {{ .Values.ca_server_port | default 5064 }} + targetPort: {{ .Values.ca_server_port | default 5064 }} + protocol: TCP + - name: ca-server-udp + port: {{ .Values.ca_server_port | default 5064 }} + targetPort: {{ .Values.ca_server_port | default 5064 }} + protocol: UDP + - name: ca-repeater-tcp + port: {{ add1 (.Values.ca_server_port | default 5064) }} + targetPort: {{ add1 (.Values.ca_server_port | default 5064) }} + protocol: TCP + - name: ca-repeater-udp + port: {{ add1 (.Values.ca_server_port | default 5064) }} + targetPort: {{ add1 (.Values.ca_server_port | default 5064) }} + protocol: UDP + - name: pva-server-tcp + port: {{ .Values.pva_server_port | default 5075 }} + targetPort: {{ .Values.pva_server_port | default 5075 }} + protocol: TCP + - name: pva-server-udp + port: {{ .Values.pva_server_port | default 5075 }} + targetPort: {{ .Values.pva_server_port | default 5075 }} + protocol: UDP + - name: pva-broadcast-tcp + port: {{ add1 (.Values.pva_server_port | default 5075) }} + targetPort: {{ add1 (.Values.pva_server_port | default 5075) }} + protocol: TCP + - name: pva-broadcast-udp + port: {{ add1 (.Values.pva_server_port | default 5075) }} + targetPort: {{ add1 (.Values.pva_server_port | default 5075) }} + protocol: UDP + selector: + {{- include "fastcs.selectorLabels" . | nindent 4 }} diff --git a/Charts/fastcs/templates/tests/test-connection.yaml b/Charts/fastcs/templates/tests/test-connection.yaml new file mode 100644 index 000000000..c2f6e7fbd --- /dev/null +++ b/Charts/fastcs/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "fastcs.fullname" . }}-test-connection" + labels: + {{- include "fastcs.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "fastcs.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/Charts/fastcs/values.schema.json b/Charts/fastcs/values.schema.json new file mode 100644 index 000000000..16ece7374 --- /dev/null +++ b/Charts/fastcs/values.schema.json @@ -0,0 +1,234 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "affinity": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.Affinity", + "description": "Affinity for the pod", + "required": [], + "title": "affinity", + "type": "object" + }, + "args": { + "description": "arguments to pass to the above command", + "items": { + "required": [], + "type": "string" + }, + "required": [], + "title": "args", + "type": "array" + }, + "baseIp": { + "default": "10.96.0.0/12", + "description": "Used by allocateIpFromName to allocate a fixed cluster IP for the service.\nThe default is the same for all DLS clusters.\n", + "pattern": "^(\\d{1,3}\\.){3}\\d{1,3}\\/\\d{1,2}$", + "required": [], + "title": "CIDR for services addresses.", + "type": "string" + }, + "ca_server_port": { + "default": 5064, + "description": "service port for Channel Access", + "required": [], + "title": "ca_server_port", + "type": "integer" + }, + "clusterIP": { + "default": "", + "format": "ipv4", + "required": [], + "title": "Override for the cluster IP - only needed if allocateIpFromName clashes", + "type": [ + "string", + "null" + ] + }, + "command": { + "description": "Command to run in the container (as array of arguments)", + "items": { + "required": [], + "type": "string" + }, + "required": [], + "title": "command", + "type": "array" + }, + "extra_containers": { + "description": "adds addtional containers specified by image and command", + "items": { + "additionalProperties": false, + "properties": { + "command": { + "description": "Command to run in the container (as array of arguments)", + "required": [], + "type": "array" + }, + "image": { + "description": "Container image URI", + "format": "image", + "required": [], + "type": "string" + }, + "name": { + "description": "A name for the additional container", + "required": [], + "type": "string" + } + }, + "required": [ + "name", + "image", + "command" + ], + "type": "object" + }, + "required": [], + "title": "extra_containers", + "type": "array" + }, + "global": { + "description": "Global values are values that can be accessed from any chart or subchart by exactly the same name.", + "required": [], + "title": "global", + "type": "object" + }, + "image": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.Container/properties/image", + "required": [], + "title": "image", + "type": "string" + }, + "iocConfig": { + "default": "/epics/ioc/config", + "description": "location of config folder (defaults to be the same as C++ IOCs)", + "required": [], + "title": "iocConfig", + "type": "string" + }, + "livenessProbe": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.Probe", + "required": [], + "title": "livenessProbe", + "type": "object" + }, + "nodeSelector": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.NodeSelector", + "description": "Node selector for the pod", + "required": [], + "title": "nodeSelector", + "type": [ + "object", + "null" + ] + }, + "podAnnotations": { + "additionalProperties": false, + "required": [], + "title": "Pod Annotations", + "type": "object" + }, + "podLabels": { + "additionalProperties": false, + "required": [], + "title": "Pod Labels", + "type": "object" + }, + "podSecurityContext": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.SecurityContext", + "required": [], + "title": "Pod Security Context", + "type": "object" + }, + "pva_server_port": { + "default": 5075, + "description": "service port for PV Access", + "required": [], + "title": "pva_server_port", + "type": "integer" + }, + "readinessProbe": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.Probe", + "required": [], + "title": "readinessProbe", + "type": "object" + }, + "resources": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.ResourceRequirements", + "required": [], + "title": "Resource limits and requests", + "type": "object" + }, + "securityContext": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.SecurityContext", + "required": [], + "title": "Container Security Context", + "type": "object" + }, + "service": { + "additionalProperties": false, + "description": "The service will be configured for Channel Access and PVA. Here you can override\nthe ports and also make this a LoadBalancer service if required.\n", + "properties": { + "ca_port": { + "default": 5064, + "required": [], + "title": "ca_port", + "type": "integer" + }, + "pva_port": { + "default": 5075, + "required": [], + "title": "pva_port", + "type": "integer" + }, + "type": { + "default": "ClusterIP", + "required": [], + "title": "type", + "type": "string" + } + }, + "required": [ + "type", + "ca_port", + "pva_port" + ], + "title": "service", + "type": "object" + }, + "tolerations": { + "description": "Tolerations for the pod", + "items": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.Toleration", + "required": [], + "type": "object" + }, + "required": [], + "title": "tolerations", + "type": "array" + }, + "volumeMounts": { + "items": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.VolumeMount", + "required": [], + "type": "object" + }, + "required": [], + "title": "volumeMounts", + "type": "array" + }, + "volumes": { + "description": "Additional volumes to mount in the output Deployment definition.", + "items": { + "$ref": "https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.Volume", + "required": [], + "type": "object" + }, + "required": [], + "title": "volumes", + "type": "array" + } + }, + "required": [], + "type": "object" +} diff --git a/Charts/fastcs/values.yaml b/Charts/fastcs/values.yaml new file mode 100644 index 000000000..81bc961e4 --- /dev/null +++ b/Charts/fastcs/values.yaml @@ -0,0 +1,198 @@ +# yaml-language-server: $schema=values.schema.json + +# Default values for fastcs-instance chart. +# +# helm-schema annotations describe the schema for values files. +# see https://github.com/dadav/helm-schema +# Generate the values schema with `generate-schema.sh` +# +# This file itself adheres to the schema it describes (to help writing the annotations). + +# @schema +# title: image +# type: string +# $ref: https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.Container/properties/image +# @schema +image: "" + +# @schema +# description: Command to run in the container (as array of arguments) +# type: array +# items: +# type: string +# required: false +# @schema +command: [] + +# @schema +# description: arguments to pass to the above command +# type: array +# items: +# type: string +# required: false +# @schema +args: [] + +# @schema +# description: location of config folder (defaults to be the same as C++ IOCs) +# type: string +# required: false +# @schema +iocConfig: /epics/ioc/config + +# @schema +# description: service port for Channel Access +# type: integer +# required: false +# @schema +ca_server_port: 5064 +# @schema +# description: service port for PV Access +# type: integer +# required: false +# @schema +pva_server_port: 5075 + +# @schema +# title: CIDR for services addresses. +# description: | +# Used by allocateIpFromName to allocate a fixed cluster IP for the service. +# The default is the same for all DLS clusters. +# type: string +# pattern: ^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$ +# required: false +# @schema +baseIp: 10.96.0.0/12 +# @schema +# title: Override for the cluster IP - only needed if allocateIpFromName clashes +# type: [string, null] +# format: ipv4 +# required: false +# @schema +clusterIP: + +# @schema +# description: | +# The service will be configured for Channel Access and PVA. Here you can override +# the ports and also make this a LoadBalancer service if required. +# type: object +# required: false +# @schema +service: + type: ClusterIP + ca_port: 5064 + pva_port: 5075 + +# @schema +# description: adds addtional containers specified by image and command +# type: array +# items: +# type: object +# properties: +# name: +# type: string +# description: A name for the additional container +# image: +# type: string +# format: image +# description: Container image URI +# command: +# type: array +# description: Command to run in the container (as array of arguments) +# required: [name, image, command] +# additionalProperties: false +# @schema +extra_containers: [] + +# @schema +# title: Pod Annotations +# type: object +# @schema +podAnnotations: {} +# @schema +# title: Pod Labels +# type: object +# @schema +podLabels: {} + +# @schema +# title: Pod Security Context +# type: object +# $ref: https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.SecurityContext +# @schema +podSecurityContext: {} + +# @schema +# title: Container Security Context +# type: object +# $ref: https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.SecurityContext +# @schema +securityContext: {} + +# @schema +# title: Resource limits and requests +# type: object +# $ref: https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.ResourceRequirements +# @schema +resources: {} + +# @schema +# title: livenessProbe +# type: object +# $ref: https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.Probe +# @schema +livenessProbe: {} +# @schema +# title: readinessProbe +# type: object +# $ref: https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.Probe +# @schema +readinessProbe: {} + +# @schema +# title: volumes +# description: Additional volumes to mount in the output Deployment definition. +# type: array +# items: +# type: object +# $ref: https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.Volume +# @schema +volumes: [] + +# @schema +# title: volumeMounts +# type: array +# items: +# type: object +# $ref: https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.VolumeMount +# @schema +volumeMounts: [] + +# TODO - I had problems with schema checking at helm chart ArgoCD deploy time +# TODO - it seemed that {} for nodeSelector was illegal, so I added null as an option +# TODO - I'm not sure why this was needed but not for other object fields with k8s schemas +# @schema +# title: nodeSelector +# description: Node selector for the pod +# type: [object, null] +# $ref: https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.NodeSelector +# @schema +nodeSelector: + +# @schema +# title: tolerations +# description: Tolerations for the pod +# type: array +# items: +# type: object +# $ref: https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.Toleration +# @schema +tolerations: [] + +# @schema +# title: affinity +# description: Affinity for the pod +# type: object +# $ref: https://kubernetesjsonschema.dev/v1.18.1/_definitions.json#/definitions/io.k8s.api.core.v1.Affinity +# @schema +affinity: {} diff --git a/generate-schemas.sh b/generate-schemas.sh new file mode 100755 index 000000000..abe52114b --- /dev/null +++ b/generate-schemas.sh @@ -0,0 +1,15 @@ +#!/bin/env bash + +set -euo pipefail + +this_dir=$(dirname "$0") +cd "$this_dir" + +helm schema -v || helm plugin install https://github.com/dadav/helm-schema + +for chart in Charts/*; do + helm schema -c $chart + ln -fs ../$chart/values.schema.json schemas/$(basename $chart).schema.json + # add a newline to the end of the schema file for pre-commit compliance + echo >> schemas/$(basename $chart).schema.json +done