@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.
- package/.github/workflows/release-publish.yml +1 -1
- package/README.md +5 -0
- package/kubernetes.js +102 -3
- package/package.json +1 -1
|
@@ -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@
|
|
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/
|
|
781
|
-
validate: '^(([a-
|
|
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) {
|