From df01fb33040058b181620f184aa3779d33bf371b Mon Sep 17 00:00:00 2001 From: Stavros Kois <47820033+stavros-k@users.noreply.github.com> Date: Thu, 6 Jul 2023 15:16:27 +0300 Subject: [PATCH] NAS-122777 / 23.10 / Add `node-red` to `community` train (#1323) * add node-red - initial commit * add templtes * add ci values and questions * run single test * all but one * add trace logging * hmmmm * root * add tests * no 18 for now * update questions and chart.yaml * fix tests * bump common --- library/ix-dev/community/node-red/Chart.lock | 6 + library/ix-dev/community/node-red/Chart.yaml | 25 ++ library/ix-dev/community/node-red/README.md | 8 + .../ix-dev/community/node-red/app-readme.md | 8 + .../node-red/charts/common-1.0.10.tgz | Bin 0 -> 56386 bytes .../node-red/ci/basic-node14-values.yaml | 10 + .../ci/basic-node14minimal-values.yaml | 10 + .../node-red/ci/basic-node16-values.yaml | 10 + .../ci/basic-node16minimal-values.yaml | 10 + .../node-red/ci/basic-node18-values.yaml | 10 + .../ci/basic-node18minimal-values.yaml | 10 + .../community/node-red/ci/extra-values.yaml | 21 ++ .../community/node-red/ci/hostNet-values.yaml | 8 + library/ix-dev/community/node-red/item.yaml | 7 + .../ix-dev/community/node-red/metadata.yaml | 8 + .../ix-dev/community/node-red/questions.yaml | 248 ++++++++++++++++++ .../community/node-red/templates/NOTES.txt | 1 + .../community/node-red/templates/_nodered.tpl | 60 +++++ .../node-red/templates/_persistence.tpl | 34 +++ .../community/node-red/templates/_portal.tpl | 12 + .../community/node-red/templates/_service.tpl | 15 ++ .../community/node-red/templates/common.yaml | 11 + .../community/node-red/upgrade_info.json | 11 + .../community/node-red/upgrade_strategy | 71 +++++ library/ix-dev/community/node-red/values.yaml | 45 ++++ 25 files changed, 659 insertions(+) create mode 100644 library/ix-dev/community/node-red/Chart.lock create mode 100644 library/ix-dev/community/node-red/Chart.yaml create mode 100644 library/ix-dev/community/node-red/README.md create mode 100644 library/ix-dev/community/node-red/app-readme.md create mode 100644 library/ix-dev/community/node-red/charts/common-1.0.10.tgz create mode 100644 library/ix-dev/community/node-red/ci/basic-node14-values.yaml create mode 100644 library/ix-dev/community/node-red/ci/basic-node14minimal-values.yaml create mode 100644 library/ix-dev/community/node-red/ci/basic-node16-values.yaml create mode 100644 library/ix-dev/community/node-red/ci/basic-node16minimal-values.yaml create mode 100644 library/ix-dev/community/node-red/ci/basic-node18-values.yaml create mode 100644 library/ix-dev/community/node-red/ci/basic-node18minimal-values.yaml create mode 100644 library/ix-dev/community/node-red/ci/extra-values.yaml create mode 100644 library/ix-dev/community/node-red/ci/hostNet-values.yaml create mode 100644 library/ix-dev/community/node-red/item.yaml create mode 100644 library/ix-dev/community/node-red/metadata.yaml create mode 100644 library/ix-dev/community/node-red/questions.yaml create mode 100644 library/ix-dev/community/node-red/templates/NOTES.txt create mode 100644 library/ix-dev/community/node-red/templates/_nodered.tpl create mode 100644 library/ix-dev/community/node-red/templates/_persistence.tpl create mode 100644 library/ix-dev/community/node-red/templates/_portal.tpl create mode 100644 library/ix-dev/community/node-red/templates/_service.tpl create mode 100644 library/ix-dev/community/node-red/templates/common.yaml create mode 100644 library/ix-dev/community/node-red/upgrade_info.json create mode 100644 library/ix-dev/community/node-red/upgrade_strategy create mode 100644 library/ix-dev/community/node-red/values.yaml diff --git a/library/ix-dev/community/node-red/Chart.lock b/library/ix-dev/community/node-red/Chart.lock new file mode 100644 index 0000000000..023945668a --- /dev/null +++ b/library/ix-dev/community/node-red/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: common + repository: file://../../../common + version: 1.0.10 +digest: sha256:06709bd6d9a8dbb721eec437a52f8c24aeecefbbc57e6f8617e53b29570d5512 +generated: "2023-07-05T19:34:00.610088429+03:00" diff --git a/library/ix-dev/community/node-red/Chart.yaml b/library/ix-dev/community/node-red/Chart.yaml new file mode 100644 index 0000000000..b8f3db59e1 --- /dev/null +++ b/library/ix-dev/community/node-red/Chart.yaml @@ -0,0 +1,25 @@ +name: node-red +description: Node-RED is a programming tool for wiring together hardware devices, APIs and online services in new and interesting ways. +annotations: + title: Node-RED +type: application +version: 1.0.0 +apiVersion: v2 +appVersion: '3.0.2' +kubeVersion: '>=1.16.0-0' +maintainers: + - name: truenas + url: https://www.truenas.com/ + email: dev@ixsystems.com +dependencies: + - name: common + repository: file://../../../common + version: 1.0.10 +home: https://nodered.org +icon: https://avatars.githubusercontent.com/u/5375661 +sources: + - https://github.com/node-red/node-red-docker + - https://github.com/truenas/charts/tree/master/community/node-red + - https://nodered.org +keywords: + - automation diff --git a/library/ix-dev/community/node-red/README.md b/library/ix-dev/community/node-red/README.md new file mode 100644 index 0000000000..eb45e862ee --- /dev/null +++ b/library/ix-dev/community/node-red/README.md @@ -0,0 +1,8 @@ +# Node-RED + +[Node-RED](https://nodered.org) is a programming tool for wiring together hardware devices, APIs and online services in new and interesting ways. + +> When application is installed, a container will be launched with **root** privileges. +> This is required in order to apply the correct permissions to the `Node-RED` directories. +> Afterward, the `Node-RED` container will run as a **non**-root user (`1000`). +> All mounted storage(s) will be `chown`ed only if the parent directory does not match the configured user. diff --git a/library/ix-dev/community/node-red/app-readme.md b/library/ix-dev/community/node-red/app-readme.md new file mode 100644 index 0000000000..eb45e862ee --- /dev/null +++ b/library/ix-dev/community/node-red/app-readme.md @@ -0,0 +1,8 @@ +# Node-RED + +[Node-RED](https://nodered.org) is a programming tool for wiring together hardware devices, APIs and online services in new and interesting ways. + +> When application is installed, a container will be launched with **root** privileges. +> This is required in order to apply the correct permissions to the `Node-RED` directories. +> Afterward, the `Node-RED` container will run as a **non**-root user (`1000`). +> All mounted storage(s) will be `chown`ed only if the parent directory does not match the configured user. diff --git a/library/ix-dev/community/node-red/charts/common-1.0.10.tgz b/library/ix-dev/community/node-red/charts/common-1.0.10.tgz new file mode 100644 index 0000000000000000000000000000000000000000..27255b809efc82a5cb8dfcb895f4c0cf54e9217b GIT binary patch literal 56386 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 literal 0 HcmV?d00001 diff --git a/library/ix-dev/community/node-red/ci/basic-node14-values.yaml b/library/ix-dev/community/node-red/ci/basic-node14-values.yaml new file mode 100644 index 0000000000..95f53f296d --- /dev/null +++ b/library/ix-dev/community/node-red/ci/basic-node14-values.yaml @@ -0,0 +1,10 @@ +noderedConfig: + imageSelector: node14Image + +noderedNetwork: + webPort: 31000 + +noderedStorage: + data: + type: hostPath + hostPath: /mnt/{{ .Release.Namespace }}/data diff --git a/library/ix-dev/community/node-red/ci/basic-node14minimal-values.yaml b/library/ix-dev/community/node-red/ci/basic-node14minimal-values.yaml new file mode 100644 index 0000000000..cefa757faa --- /dev/null +++ b/library/ix-dev/community/node-red/ci/basic-node14minimal-values.yaml @@ -0,0 +1,10 @@ +noderedConfig: + imageSelector: node14MinimalImage + +noderedNetwork: + webPort: 31000 + +noderedStorage: + data: + type: hostPath + hostPath: /mnt/{{ .Release.Namespace }}/data diff --git a/library/ix-dev/community/node-red/ci/basic-node16-values.yaml b/library/ix-dev/community/node-red/ci/basic-node16-values.yaml new file mode 100644 index 0000000000..5e866030f7 --- /dev/null +++ b/library/ix-dev/community/node-red/ci/basic-node16-values.yaml @@ -0,0 +1,10 @@ +noderedConfig: + imageSelector: node16Image + +noderedNetwork: + webPort: 31000 + +noderedStorage: + data: + type: hostPath + hostPath: /mnt/{{ .Release.Namespace }}/data diff --git a/library/ix-dev/community/node-red/ci/basic-node16minimal-values.yaml b/library/ix-dev/community/node-red/ci/basic-node16minimal-values.yaml new file mode 100644 index 0000000000..a2a0b645d8 --- /dev/null +++ b/library/ix-dev/community/node-red/ci/basic-node16minimal-values.yaml @@ -0,0 +1,10 @@ +noderedConfig: + imageSelector: node16MinimalImage + +noderedNetwork: + webPort: 31000 + +noderedStorage: + data: + type: hostPath + hostPath: /mnt/{{ .Release.Namespace }}/data diff --git a/library/ix-dev/community/node-red/ci/basic-node18-values.yaml b/library/ix-dev/community/node-red/ci/basic-node18-values.yaml new file mode 100644 index 0000000000..3b5b020de7 --- /dev/null +++ b/library/ix-dev/community/node-red/ci/basic-node18-values.yaml @@ -0,0 +1,10 @@ +noderedConfig: + imageSelector: node18Image + +noderedNetwork: + webPort: 31000 + +noderedStorage: + data: + type: hostPath + hostPath: /mnt/{{ .Release.Namespace }}/data diff --git a/library/ix-dev/community/node-red/ci/basic-node18minimal-values.yaml b/library/ix-dev/community/node-red/ci/basic-node18minimal-values.yaml new file mode 100644 index 0000000000..8ca25cb261 --- /dev/null +++ b/library/ix-dev/community/node-red/ci/basic-node18minimal-values.yaml @@ -0,0 +1,10 @@ +noderedConfig: + imageSelector: node18MinimalImage + +noderedNetwork: + webPort: 31000 + +noderedStorage: + data: + type: hostPath + hostPath: /mnt/{{ .Release.Namespace }}/data diff --git a/library/ix-dev/community/node-red/ci/extra-values.yaml b/library/ix-dev/community/node-red/ci/extra-values.yaml new file mode 100644 index 0000000000..06a89343bd --- /dev/null +++ b/library/ix-dev/community/node-red/ci/extra-values.yaml @@ -0,0 +1,21 @@ +noderedConfig: + safeMode: true + enableProjects: true + additionalEnvs: + - name: NODE_OPTIONS + value: --max-old-space-size=256 + +noderedNetwork: + webPort: 31000 + +noderedStorage: + data: + type: hostPath + hostPath: /mnt/{{ .Release.Namespace }}/data + additionalStorages: + - type: hostPath + hostPath: /mnt/{{ .Release.Namespace }}/customData + mountPath: /customData + - type: hostPath + hostPath: /mnt/{{ .Release.Namespace }}/customFiles + mountPath: /customFiles diff --git a/library/ix-dev/community/node-red/ci/hostNet-values.yaml b/library/ix-dev/community/node-red/ci/hostNet-values.yaml new file mode 100644 index 0000000000..f5db45e0cb --- /dev/null +++ b/library/ix-dev/community/node-red/ci/hostNet-values.yaml @@ -0,0 +1,8 @@ +noderedNetwork: + webPort: 30000 + hostNetwork: true + +noderedStorage: + data: + type: hostPath + hostPath: /mnt/{{ .Release.Namespace }}/data diff --git a/library/ix-dev/community/node-red/item.yaml b/library/ix-dev/community/node-red/item.yaml new file mode 100644 index 0000000000..f195f2e4ea --- /dev/null +++ b/library/ix-dev/community/node-red/item.yaml @@ -0,0 +1,7 @@ +icon_url: https://avatars.githubusercontent.com/u/5375661 +categories: + - productivity +screenshots: + - https://camo.githubusercontent.com/c7b6e0b937295c4d2c852130814050eb0caffac5b700ead6de21df6dbf83aa82/687474703a2f2f6e6f64657265642e6f72672f696d616765732f6e6f64652d7265642d73637265656e73686f742e706e67 +tags: + - automation diff --git a/library/ix-dev/community/node-red/metadata.yaml b/library/ix-dev/community/node-red/metadata.yaml new file mode 100644 index 0000000000..579054d506 --- /dev/null +++ b/library/ix-dev/community/node-red/metadata.yaml @@ -0,0 +1,8 @@ +runAsContext: + - userName: node-red + groupName: node-red + gid: 1000 + uid: 1000 + description: Node-RED runs as a non-root user. +capabilities: [] +hostMounts: [] diff --git a/library/ix-dev/community/node-red/questions.yaml b/library/ix-dev/community/node-red/questions.yaml new file mode 100644 index 0000000000..b9e44e4bc2 --- /dev/null +++ b/library/ix-dev/community/node-red/questions.yaml @@ -0,0 +1,248 @@ +groups: + - name: Node-RED Configuration + description: Configure Node-RED + - name: User and Group Configuration + description: Configure User and Group for Node-RED + - name: Network Configuration + description: Configure Network for Node-RED + - name: Storage Configuration + description: Configure Storage for Node-RED + - name: Resources Configuration + description: Configure Resources for Node-RED + +portals: + web_portal: + protocols: + - "$kubernetes-resource_configmap_portal_protocol" + host: + - "$kubernetes-resource_configmap_portal_host" + ports: + - "$kubernetes-resource_configmap_portal_port" + path: "$kubernetes-resource_configmap_portal_path" + +questions: + - variable: noderedConfig + label: "" + group: Node-RED Configuration + schema: + type: dict + attrs: + - variable: imageSelector + label: Node-RED Image + description: The Node-RED image to use. + schema: + type: string + default: "node16Image" + required: true + enum: + - value: node14Image + description: Node-RED on Node.js 14 + - value: node14MinimalImage + description: Node-RED Minimal on Node.js 14 + - value: node16Image + description: Node-RED on Node.js 16 + - value: node16MinimalImage + description: Node-RED Minimal on Node.js 16 + - value: node18Image + description: Node-RED on Node.js 18 + - value: node18MinimalImage + description: Node-RED Minimal on Node.js 18 + - variable: safeMode + label: Safe Mode + description: | + Starts Node-RED without starting the flows.
+ This allows you to open the flows in the editor and + make changes without the flows running.
+ When you deploy your changes, the flows are then started. + schema: + type: boolean + default: false + - variable: enableProjects + label: Enable Projects + description: | + Enable projects in Node-RED.
+ This allows you to use the projects feature in Node-RED. + schema: + type: boolean + default: false + - variable: additionalEnvs + label: Additional Environment Variables + description: Configure additional environment variables for Node-RED. + schema: + type: list + default: [] + items: + - variable: env + label: Environment Variable + schema: + type: dict + attrs: + - variable: name + label: Name + schema: + type: string + required: true + - variable: value + label: Value + schema: + type: string + required: true + + - variable: noderedNetwork + label: "" + group: Network Configuration + schema: + type: dict + attrs: + - variable: webPort + label: Web Port + description: The port for the Node-RED Web UI. + schema: + type: int + default: 30049 + min: 9000 + max: 65535 + required: true + - variable: hostNetwork + label: Host Network + description: | + Bind to the host network. It's recommended to keep this disabled.
+ schema: + type: boolean + default: false + + - variable: noderedStorage + label: "" + group: Storage Configuration + schema: + type: dict + attrs: + - variable: data + label: Node-RED Data Storage + description: The path to store Node-RED Data. + schema: + type: dict + attrs: + - variable: type + label: Type + description: | + ixVolume: Is dataset created automatically by the system.
+ Host Path: Is a path that already exists on the system. + schema: + type: string + required: true + default: "ixVolume" + enum: + - value: "hostPath" + description: Host Path (Path that already exists on the system) + - value: "ixVolume" + description: ixVolume (Dataset created automatically by the system) + - variable: datasetName + label: Dataset Name + schema: + type: string + show_if: [["type", "=", "ixVolume"]] + required: true + hidden: true + immutable: true + default: "data" + $ref: + - "normalize/ixVolume" + - variable: hostPath + label: Host Path + schema: + type: hostpath + show_if: [["type", "=", "hostPath"]] + immutable: true + required: true + - variable: additionalStorages + label: Additional Storage + description: Additional storage for Node-RED. + schema: + type: list + default: [] + items: + - variable: storageEntry + label: Storage Entry + schema: + type: dict + attrs: + - variable: type + label: Type + description: | + ixVolume: Is dataset created automatically by the system.
+ Host Path: Is a path that already exists on the system. + schema: + type: string + required: true + default: "ixVolume" + enum: + - value: "hostPath" + description: Host Path (Path that already exists on the system) + - value: "ixVolume" + description: ixVolume (Dataset created automatically by the system) + - variable: mountPath + label: Mount Path + description: The path inside the container to mount the storage. + schema: + type: path + required: true + - variable: hostPath + label: Host Path + description: The host path to use for storage. + schema: + type: hostpath + show_if: [["type", "=", "hostPath"]] + required: true + - variable: datasetName + label: Dataset Name + description: The name of the dataset to use for storage. + schema: + type: string + show_if: [["type", "=", "ixVolume"]] + required: true + immutable: true + default: "storage_entry" + $ref: + - "normalize/ixVolume" + + - variable: resources + group: Resources Configuration + label: "" + schema: + type: dict + attrs: + - variable: limits + label: Limits + schema: + type: dict + attrs: + - variable: cpu + label: CPU + description: CPU limit for Node-RED. + schema: + type: string + max_length: 6 + valid_chars: '^(0\.[1-9]|[1-9][0-9]*)(\.[0-9]|m?)$' + valid_chars_error: | + Valid CPU limit formats are
+ - Plain Integer - eg. 1
+ - Float - eg. 0.5
+ - Milicpu - eg. 500m + default: "4000m" + required: true + - variable: memory + label: Memory + description: Memory limit for Node-RED. + schema: + type: string + max_length: 12 + valid_chars: '^[1-9][0-9]*([EPTGMK]i?|e[0-9]+)?$' + valid_chars_error: | + Valid Memory limit formats are
+ - 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 + default: "8Gi" + required: true diff --git a/library/ix-dev/community/node-red/templates/NOTES.txt b/library/ix-dev/community/node-red/templates/NOTES.txt new file mode 100644 index 0000000000..ba4e01146c --- /dev/null +++ b/library/ix-dev/community/node-red/templates/NOTES.txt @@ -0,0 +1 @@ +{{ include "ix.v1.common.lib.chart.notes" $ }} diff --git a/library/ix-dev/community/node-red/templates/_nodered.tpl b/library/ix-dev/community/node-red/templates/_nodered.tpl new file mode 100644 index 0000000000..aa167f1de7 --- /dev/null +++ b/library/ix-dev/community/node-red/templates/_nodered.tpl @@ -0,0 +1,60 @@ +{{- define "nodered.workload" -}} +workload: + nodered: + enabled: true + primary: true + type: Deployment + podSpec: + hostNetwork: {{ .Values.noderedNetwork.hostNetwork }} + containers: + nodered: + enabled: true + primary: true + imageSelector: {{ .Values.noderedConfig.imageSelector }} + # https://github.com/node-red/node-red-docker/wiki/Permissions-and-Persistence + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + env: + PORT: {{ .Values.noderedNetwork.webPort }} + NODE_RED_ENABLE_SAFE_MODE: {{ .Values.noderedConfig.safeMode }} + NODE_RED_ENABLE_PROJECTS: {{ .Values.noderedConfig.enableProjects }} + {{ with .Values.noderedConfig.additionalEnvs }} + envList: + {{ range $env := . }} + - name: {{ $env.name }} + value: {{ $env.value }} + {{ end }} + {{ end }} + probes: + liveness: + enabled: true + type: exec + command: + - /bin/sh + - -c + - | + NODE_OPTIONS=--dns-result-order=ipv4first node /healthcheck.js + readiness: + enabled: true + type: exec + command: + - /bin/sh + - -c + - | + NODE_OPTIONS=--dns-result-order=ipv4first node /healthcheck.js + startup: + enabled: true + type: exec + command: + - /bin/sh + - -c + - | + NODE_OPTIONS=--dns-result-order=ipv4first node /healthcheck.js + initContainers: + {{- include "ix.v1.common.app.permissions" (dict "containerName" "01-permissions" + "UID" 1000 + "GID" 1000 + "mode" "check" + "type" "init") | nindent 8 }} +{{- end -}} diff --git a/library/ix-dev/community/node-red/templates/_persistence.tpl b/library/ix-dev/community/node-red/templates/_persistence.tpl new file mode 100644 index 0000000000..a7cc2a44b6 --- /dev/null +++ b/library/ix-dev/community/node-red/templates/_persistence.tpl @@ -0,0 +1,34 @@ +{{- define "nodered.persistence" -}} +persistence: + data: + enabled: true + type: {{ .Values.noderedStorage.data.type }} + datasetName: {{ .Values.noderedStorage.data.datasetName | default "" }} + hostPath: {{ .Values.noderedStorage.data.hostPath | default "" }} + targetSelector: + nodered: + nodered: + mountPath: /data + 01-permissions: + mountPath: /mnt/directories/data + tmp: + enabled: true + type: emptyDir + targetSelector: + nodered: + nodered: + mountPath: /tmp + {{- range $idx, $storage := .Values.noderedStorage.additionalStorages }} + {{ printf "nodered-%v" (int $idx) }}: + enabled: true + type: {{ $storage.type }} + datasetName: {{ $storage.datasetName | default "" }} + hostPath: {{ $storage.hostPath | default "" }} + targetSelector: + nodered: + nodered: + mountPath: {{ $storage.mountPath }} + 01-permissions: + mountPath: /mnt/directories{{ $storage.mountPath }} + {{- end }} +{{- end -}} diff --git a/library/ix-dev/community/node-red/templates/_portal.tpl b/library/ix-dev/community/node-red/templates/_portal.tpl new file mode 100644 index 0000000000..2dc1a2cf8f --- /dev/null +++ b/library/ix-dev/community/node-red/templates/_portal.tpl @@ -0,0 +1,12 @@ +{{- define "nodered.portal" -}} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: portal +data: + path: "/" + port: {{ .Values.noderedNetwork.webPort | quote }} + protocol: http + host: $node_ip +{{- end -}} diff --git a/library/ix-dev/community/node-red/templates/_service.tpl b/library/ix-dev/community/node-red/templates/_service.tpl new file mode 100644 index 0000000000..d741a92c0d --- /dev/null +++ b/library/ix-dev/community/node-red/templates/_service.tpl @@ -0,0 +1,15 @@ +{{- define "nodered.service" -}} +service: + nodered: + enabled: true + primary: true + type: NodePort + targetSelector: nodered + ports: + webui: + enabled: true + primary: true + port: {{ .Values.noderedNetwork.webPort }} + nodePort: {{ .Values.noderedNetwork.webPort }} + targetSelector: nodered +{{- end -}} diff --git a/library/ix-dev/community/node-red/templates/common.yaml b/library/ix-dev/community/node-red/templates/common.yaml new file mode 100644 index 0000000000..bdbdb312ab --- /dev/null +++ b/library/ix-dev/community/node-red/templates/common.yaml @@ -0,0 +1,11 @@ +{{- include "ix.v1.common.loader.init" . -}} + +{{/* Merge the templates with Values */}} +{{- $_ := mustMergeOverwrite .Values (include "nodered.workload" $ | fromYaml) -}} +{{- $_ := mustMergeOverwrite .Values (include "nodered.service" $ | fromYaml) -}} +{{- $_ := mustMergeOverwrite .Values (include "nodered.persistence" $ | fromYaml) -}} + +{{/* Create the configmap for portal manually*/}} +{{- include "nodered.portal" $ -}} + +{{- include "ix.v1.common.loader.apply" . -}} diff --git a/library/ix-dev/community/node-red/upgrade_info.json b/library/ix-dev/community/node-red/upgrade_info.json new file mode 100644 index 0000000000..1ed065fbde --- /dev/null +++ b/library/ix-dev/community/node-red/upgrade_info.json @@ -0,0 +1,11 @@ +{ + "filename": "values.yaml", + "keys": [ + "node18Image", + "node18MinimalImage", + "node16Image", + "node16MinimalImage", + "node14Image", + "node14MinimalImage" + ] +} diff --git a/library/ix-dev/community/node-red/upgrade_strategy b/library/ix-dev/community/node-red/upgrade_strategy new file mode 100644 index 0000000000..cdb1794e38 --- /dev/null +++ b/library/ix-dev/community/node-red/upgrade_strategy @@ -0,0 +1,71 @@ +#!/usr/bin/python3 +import json +import re +import sys + +from catalog_update.upgrade_strategy import semantic_versioning + +RE_STABLE_VERSION_BASE = r'\d+\.\d+\.\d+' +ENUMS = { + 'node18Image': { + 'RE_STABLE_VERSION': re.compile(rf'{RE_STABLE_VERSION_BASE}-18'), + 'STRIP_TEXT': '-18' + }, + 'node18MinimalImage': { + 'RE_STABLE_VERSION': re.compile(rf'{RE_STABLE_VERSION_BASE}-18-minimal'), + 'STRIP_TEXT': '-18-minimal' + }, + 'node16Image': { + 'RE_STABLE_VERSION': re.compile(rf'{RE_STABLE_VERSION_BASE}-16'), + 'STRIP_TEXT': '-16' + }, + 'node16MinimalImage': { + 'RE_STABLE_VERSION': re.compile(rf'{RE_STABLE_VERSION_BASE}-16-minimal'), + 'STRIP_TEXT': '-16-minimal' + }, + 'node14Image': { + 'RE_STABLE_VERSION': re.compile(rf'{RE_STABLE_VERSION_BASE}-14'), + 'STRIP_TEXT': '-14' + }, + 'node14MinimalImage': { + 'RE_STABLE_VERSION': re.compile(rf'{RE_STABLE_VERSION_BASE}-14-minimal'), + 'STRIP_TEXT': '-14-minimal' + } +} + + +def newer_mapping(image_tags): + output = { + "tags": {}, + "app_version": "" + } + + for key in image_tags.keys(): + STRIP_TEXT = ENUMS[key].get('STRIP_TEXT', None) if key in ENUMS else None + RE_STABLE_VERSION = ENUMS[key].get('RE_STABLE_VERSION', None) if key in ENUMS else None + + if (STRIP_TEXT is None) or (RE_STABLE_VERSION is None): + continue + + tags = {t.strip(STRIP_TEXT): t for t in image_tags[key] if RE_STABLE_VERSION.fullmatch(t)} + version = semantic_versioning(list(tags)) + + if not version: + continue + + # 16 is the "default" (Also tied to latest tag) + if key == 'node16Image': + output['app_version'] = version + + output['tags'][key] = tags[version] + + return output + + +if __name__ == '__main__': + try: + versions_json = json.loads(sys.stdin.read()) + except ValueError: + raise ValueError('Invalid json specified') + + print(json.dumps(newer_mapping(versions_json))) diff --git a/library/ix-dev/community/node-red/values.yaml b/library/ix-dev/community/node-red/values.yaml new file mode 100644 index 0000000000..6d251c571a --- /dev/null +++ b/library/ix-dev/community/node-red/values.yaml @@ -0,0 +1,45 @@ +node18Image: + repository: nodered/node-red + pullPolicy: IfNotPresent + tag: '3.0.2-18' +node18MinimalImage: + repository: nodered/node-red + pullPolicy: IfNotPresent + tag: '3.0.2-18-minimal' +node16Image: + repository: nodered/node-red + pullPolicy: IfNotPresent + tag: '3.0.2-16' +node16MinimalImage: + repository: nodered/node-red + pullPolicy: IfNotPresent + tag: '3.0.2-16-minimal' +node14Image: + repository: nodered/node-red + pullPolicy: IfNotPresent + tag: '3.0.2-14' +node14MinimalImage: + repository: nodered/node-red + pullPolicy: IfNotPresent + tag: '3.0.2-14-minimal' + +resources: + limits: + cpu: 4000m + memory: 8Gi + +noderedConfig: + imageSelector: node16Image + safeMode: false + enableProjects: false + additionalEnvs: [] + +noderedNetwork: + webPort: 30049 + hostNetwork: false + +noderedStorage: + data: + type: ixVolume + datasetName: data + additionalStorages: []