@flowfuse/driver-kubernetes 2.22.1 → 2.22.2-6911c7c-202510201047.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,7 @@ jobs:
9
9
  runs-on: ubuntu-latest
10
10
  steps:
11
11
  - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
12
- - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
12
+ - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
13
13
  with:
14
14
  node-version: 18
15
15
  - run: npm ci
package/README.md CHANGED
@@ -44,6 +44,10 @@ driver:
44
44
  - `projectSelector` a list of labels that should be used to select which nodes Project Pods
45
45
  should run on
46
46
  - `projectLabels` a list of custom labels that should be applied to all resources created for Projects (Pods, Services, Ingresses, PVCs)
47
+ - `projectProbes` optional configuration for liveness, readiness and startup probes for project containers
48
+ - `projectProbes.livenessProbe` custom liveness probe configuration (default not set)
49
+ - `projectProbes.readinessProbe` custom readiness probe configuration (default not set)
50
+ - `projectProbes.startupProbe` custom startup probe configuration (default not set)
47
51
  - `cloudProvider` normally not set, but can be `aws` This triggers the adding of
48
52
  AWS EKS specific annotation for ALB Ingress. or `openshift` to allow running on OpenShift (Enterprise license only)
49
53
  - `privateCA` name of ConfigMap holding PEM CA Cert Bundle (file name `certs.pem`) Optional
@@ -61,6 +65,7 @@ AWS EKS specific annotation for ALB Ingress. or `openshift` to allow running on
61
65
  - `storage.storageClassEFSTag` Used instead of `storage.storageClass` when needing to shard across multiple EFS file systems (default not set)
62
66
  - `storage.size` Size of the volume to request (default not set)
63
67
  - `podSecurityContext` Settings linked to the [security context of the pod](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)
68
+ - `containerSecurityContext` Settings linked to the [security context of the container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)
64
69
  - `service.type` Type of service to create for the editor (allowed `ClusterIP` or `NodePort`, default `ClusterIP`)
65
70
 
66
71
  Expects to pick up K8s credentials from the environment
package/kubernetes.js CHANGED
@@ -194,6 +194,14 @@ const createDeployment = async (project, options) => {
194
194
  this._app.log.info('[k8s] OpenShift, removing PodSecurityContext')
195
195
  }
196
196
 
197
+ if (this._app.config.driver.options?.containerSecurityContext) {
198
+ localPod.spec.containers[0].securityContext = this._app.config.driver.options.containerSecurityContext
199
+ this._app.log.info(`[k8s] Using custom ContainerSecurityContext ${JSON.stringify(this._app.config.driver.options.containerSecurityContext)}`)
200
+ } else if (this._app.license.active() && this._cloudProvider === 'openshift') {
201
+ localPod.spec.containers[0].securityContext = {}
202
+ this._app.log.info('[k8s] OpenShift, removing ContainerSecurityContext')
203
+ }
204
+
197
205
  if (stack.memory && stack.cpu) {
198
206
  localPod.spec.containers[0].resources.requests.memory = `${stack.memory}Mi`
199
207
  // increase limit to give npm more room to run in
@@ -213,6 +221,16 @@ const createDeployment = async (project, options) => {
213
221
  }
214
222
  }
215
223
 
224
+ if (this._app.config.driver.options?.projectProbes?.livenessProbe) {
225
+ localPod.spec.containers[0].livenessProbe = this._app.config.driver.options.projectProbes.livenessProbe
226
+ }
227
+ if (this._app.config.driver.options?.projectProbes?.readinessProbe) {
228
+ localPod.spec.containers[0].readinessProbe = this._app.config.driver.options.projectProbes.readinessProbe
229
+ }
230
+ if (this._app.config.driver.options?.projectProbes?.startupProbe) {
231
+ localPod.spec.containers[0].startupProbe = this._app.config.driver.options.projectProbes.startupProbe
232
+ }
233
+
216
234
  const ha = await project.getSetting('ha')
217
235
  if (ha?.replicas > 1) {
218
236
  localDeployment.spec.replicas = ha.replicas
@@ -261,8 +279,37 @@ const createIngress = async (project, options) => {
261
279
 
262
280
  const localIngress = JSON.parse(JSON.stringify(ingressTemplate))
263
281
 
282
+ let addIngressTls = false
283
+
264
284
  if (this._certManagerIssuer) {
265
285
  localIngress.metadata.annotations['cert-manager.io/cluster-issuer'] = this._certManagerIssuer
286
+ addIngressTls = true
287
+
288
+ // Add non-cert-manager annotations from projectIngressAnnotations if they exist
289
+ if (this._projectIngressAnnotations) {
290
+ Object.keys(this._projectIngressAnnotations).forEach((key) => {
291
+ if (!key.startsWith('cert-manager.io/')) {
292
+ localIngress.metadata.annotations[key] = this._projectIngressAnnotations[key]
293
+ }
294
+ })
295
+ }
296
+ } else if (this._projectIngressAnnotations) {
297
+ const hasCertManagerAnnotation = Object.keys(this._projectIngressAnnotations).some(key =>
298
+ key.startsWith('cert-manager.io/')
299
+ )
300
+
301
+ if (hasCertManagerAnnotation) {
302
+ addIngressTls = true
303
+ }
304
+
305
+ // Add all annotations from projectIngressAnnotations
306
+ Object.keys(this._projectIngressAnnotations).forEach((key) => {
307
+ localIngress.metadata.annotations[key] = this._projectIngressAnnotations[key]
308
+ })
309
+ }
310
+
311
+ // Add TLS configuration if needed
312
+ if (addIngressTls) {
266
313
  localIngress.spec.tls = [
267
314
  {
268
315
  hosts: [
@@ -309,8 +356,37 @@ const createCustomIngress = async (project, hostname, options) => {
309
356
  customIngress.spec.rules[0].host = hostname
310
357
  customIngress.spec.rules[0].http.paths[0].backend.service.name = `${prefix}${project.safeName}`
311
358
 
359
+ let addCustomIngressTls = false
360
+
312
361
  if (this._customHostname?.certManagerIssuer) {
313
362
  customIngress.metadata.annotations['cert-manager.io/cluster-issuer'] = this._customHostname.certManagerIssuer
363
+ addCustomIngressTls = true
364
+
365
+ // Add non-cert-manager annotations from projectIngressAnnotations if they exist
366
+ if (this._customHostname?.ingressAnnotations) {
367
+ Object.keys(this._customHostname?.ingressAnnotations).forEach((key) => {
368
+ if (!key.startsWith('cert-manager.io/')) {
369
+ customIngress.metadata.annotations[key] = this._customHostname?.ingressAnnotations[key]
370
+ }
371
+ })
372
+ }
373
+ } else if (this._customHostname?.ingressAnnotations) {
374
+ const hasCertManagerAnnotation = Object.keys(this._customHostname?.ingressAnnotations).some(key =>
375
+ key.startsWith('cert-manager.io/')
376
+ )
377
+
378
+ if (hasCertManagerAnnotation) {
379
+ addCustomIngressTls = true
380
+ }
381
+
382
+ // Add all annotations from projectIngressAnnotations
383
+ Object.keys(this._customHostname?.ingressAnnotations).forEach((key) => {
384
+ customIngress.metadata.annotations[key] = this._customHostname?.ingressAnnotations[key]
385
+ })
386
+ }
387
+
388
+ // Add TLS configuration if needed
389
+ if (addCustomIngressTls) {
314
390
  customIngress.spec.tls = [
315
391
  {
316
392
  hosts: [
@@ -628,6 +704,7 @@ module.exports = {
628
704
  this._k8sDelay = this._app.config.driver.options?.k8sDelay || 1000
629
705
  this._k8sRetries = this._app.config.driver.options?.k8sRetries || 10
630
706
  this._certManagerIssuer = this._app.config.driver.options?.certManagerIssuer
707
+ this._projectIngressAnnotations = this._app.config.driver.options?.projectIngressAnnotations
631
708
  this._logPassthrough = this._app.config.driver.options?.logPassthrough || false
632
709
  this._cloudProvider = this._app.config.driver.options?.cloudProvider
633
710
  if (this._app.config.driver.options?.customHostname?.enabled) {
@@ -777,8 +854,8 @@ module.exports = {
777
854
  },
778
855
  container: {
779
856
  label: 'Container Location',
780
- // taken from https://stackoverflow.com/a/62964157
781
- validate: '^(([a-z0-9]|[a-z0-9][a-z0-9\\-]*[a-z0-9])\\.)*([a-z0-9]|[a-z0-9][a-z0-9\\-]*[a-z0-9])(:[0-9]+\\/)?(?:[0-9a-z-]+[/@])(?:([0-9a-z-]+))[/@]?(?:([0-9a-z-]+))?(?::[a-z0-9\\.-]+)?$',
857
+ // taken from https://stackoverflow.com/a/74073589
858
+ validate: '^((?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])(?:(?:\\.(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]))+)?(?::[0-9]+)?/)?[a-z0-9]+(?:(?:(?:[._]|__|[-]*)[a-z0-9]+)+)?(?:(?:/[a-z0-9]+(?:(?:(?:[._]|__|[-]*)[a-z0-9]+)+)?)+)?)(?::([\\w][\\w.-]{0,127}))?(?:@([A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}))?$',
782
859
  invalidMessage: 'Invalid value - must be a Docker image',
783
860
  description: 'Container image location, can include a tag'
784
861
  }
@@ -828,6 +905,17 @@ module.exports = {
828
905
  } catch (err) {
829
906
  this._app.log.error(`[k8s] Instance ${project.id} - error deleting tls secret: ${err.toString()} ${err.stack}`)
830
907
  }
908
+ } else if (this._projectIngressAnnotations) {
909
+ const hasCertManagerAnnotation = Object.keys(this._projectIngressAnnotations).some(key =>
910
+ key.startsWith('cert-manager.io/')
911
+ )
912
+ if (hasCertManagerAnnotation) {
913
+ try {
914
+ await this._k8sApi.deleteNamespacedSecret({ name: project.safeName, namespace: this._namespace })
915
+ } catch (err) {
916
+ this._app.log.error(`[k8s] Instance ${project.id} - error deleting tls secret: ${err.toString()} ${err.stack}`)
917
+ }
918
+ }
831
919
  }
832
920
 
833
921
  if (this._customHostname?.enabled) {
@@ -959,6 +1047,17 @@ module.exports = {
959
1047
  } catch (err) {
960
1048
  this._app.log.error(`[k8s] Instance ${project.id} - error deleting tls secret: ${err.toString()}`)
961
1049
  }
1050
+ } else if (this._projectIngressAnnotations) {
1051
+ const hasCertManagerAnnotation = Object.keys(this._projectIngressAnnotations).some(key =>
1052
+ key.startsWith('cert-manager.io/')
1053
+ )
1054
+ if (hasCertManagerAnnotation) {
1055
+ try {
1056
+ await this._k8sApi.deleteNamespacedSecret({ name: project.safeName, namespace: this._namespace })
1057
+ } catch (err) {
1058
+ this._app.log.error(`[k8s] Instance ${project.id} - error deleting tls secret: ${err.toString()}`)
1059
+ }
1060
+ }
962
1061
  }
963
1062
  if (this._customHostname?.enabled) {
964
1063
  try {
@@ -966,7 +1065,7 @@ module.exports = {
966
1065
  } catch (err) {
967
1066
  this._app.log.error(`[k8s] Instance ${project.id} - error deleting custom ingress: ${err.toString()}`)
968
1067
  }
969
- if (this._customHostname?.certManagerIssuer) {
1068
+ if (this._customHostname?.certManagerIssuer || this._customHostname?.certManagerAnnotations) {
970
1069
  try {
971
1070
  await this._k8sApi.deleteNamespacedSecret({ name: `${project.safeName}-custom`, namespace: this._namespace })
972
1071
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowfuse/driver-kubernetes",
3
- "version": "2.22.1",
3
+ "version": "2.22.2-6911c7c-202510201047.0",
4
4
  "description": "Kubernetes driver for FlowFuse",
5
5
  "main": "kubernetes.js",
6
6
  "scripts": {