Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@flowfuse/driver-kubernetes

Package Overview
Dependencies
Maintainers
3
Versions
101
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@flowfuse/driver-kubernetes - npm Package Compare versions

Comparing version 2.4.1-d12c406-202406041336.0 to 2.5.1-6c985ff-202406060934.0

7

CHANGELOG.md

@@ -0,1 +1,8 @@

#### 2.5.0: Release
- Custom Hostname support (#151) @hardillb
- Bump actions/setup-node from 1 to 4 (#159) @app/dependabot
- Bump actions/checkout from 1 to 4 (#158) @app/dependabot
- Enable dependabot for github actions (#154) @ppawlowski
#### 2.4.0: Release

@@ -2,0 +9,0 @@

194

kubernetes.js

@@ -134,2 +134,32 @@ const got = require('got')

const customIngressTemplate = {
apiVersion: 'networking.k8s.io/v1',
kind: 'Ingress',
metadata: {
annotations: {}
},
spec: {
rules: [
{
http: {
paths: [
{
pathType: 'Prefix',
path: '/',
backend: {
service: {
port: { number: 1880 }
}
}
}
]
}
}
],
tls: [
]
}
}
const createDeployment = async (project, options) => {

@@ -344,2 +374,46 @@ const stack = project.ProjectStack.properties

const createCustomIngress = async (project, hostname, options) => {
const prefix = project.safeName.match(/^[0-9]/) ? 'srv-' : ''
const url = new URL(project.url)
url.host = hostname
// exposedData available for annotation replacements
const exposedData = {
serviceName: `${prefix}${project.safeName}`,
instanceURL: url.href,
instanceHost: url.host,
instanceProtocol: url.protocol
}
this._app.log.info('K8S DRIVER: start custom hostname ingress template')
const customIngress = JSON.parse(JSON.stringify(customIngressTemplate))
customIngress.metadata.name = `${project.safeName}-custom`
customIngress.spec.rules[0].host = hostname
customIngress.spec.rules[0].http.paths[0].backend.service.name = `${prefix}${project.safeName}`
if (this._customHostname?.certManagerIssuer) {
customIngress.metadata.annotations['cert-manager.io/cluster-issuer'] = this._customHostname.certManagerIssuer
customIngress.spec.tls = [
{
hosts: [
hostname
],
secretName: `${project.safeName}-custom`
}
]
}
// process annotations with potential replacements
Object.keys(customIngress.metadata.annotations).forEach((key) => {
customIngress.metadata.annotations[key] = mustache(customIngress.metadata.annotations[key], exposedData)
})
if (this._customHostname?.ingressClass) {
customIngress.spec.ingressClassName = `${this._customHostname.ingressClass}`
}
return customIngress
}
const createProject = async (project, options) => {

@@ -357,3 +431,3 @@ const namespace = this._app.config.driver.options.projectNamespace || 'flowforge'

// If deployment exists, perform an upgrade
this._app.log.warn(`[k8s] Deployment for project ${project.id} already exists. Upgrading deployment`)
this._app.log.warn(`[k8s] Deployment for instance ${project.id} already exists. Upgrading deployment`)
const result = await this._k8sAppApi.readNamespacedDeployment(project.safeName, namespace)

@@ -369,3 +443,3 @@

// Log other errors and rethrow them for additional higher-level handling
this._app.log.error(`[k8s] Unexpected error creating deployment for project ${project.id}.`)
this._app.log.error(`[k8s] Unexpected error creating deployment for instance ${project.id}.`)
this._app.log.error(`[k8s] deployment ${JSON.stringify(localDeployment, undefined, 2)}`)

@@ -390,3 +464,3 @@ this._app.log.error(err)

clearInterval(pollInterval)
this._app.log.error(`[k8s] Project ${project.id} - timeout waiting for Deployment`)
this._app.log.error(`[k8s] Instance ${project.id} - timeout waiting for Deployment`)
reject(new Error('Timed out to creating Deployment'))

@@ -402,6 +476,6 @@ }

if (err.statusCode === 409) {
this._app.log.warn(`[k8s] Service for project ${project.id} already exists, proceeding...`)
this._app.log.warn(`[k8s] Service for instance ${project.id} already exists, proceeding...`)
} else {
if (project.state !== 'suspended') {
this._app.log.error(`[k8s] Project ${project.id} - error creating service: ${err.toString()}`)
this._app.log.error(`[k8s] Instance ${project.id} - error creating service: ${err.toString()}`)
throw err

@@ -424,3 +498,3 @@ }

clearInterval(pollInterval)
this._app.log.error(`[k8s] Project ${project.id} - timeout waiting for Service`)
this._app.log.error(`[k8s] Instance ${project.id} - timeout waiting for Service`)
reject(new Error('Timed out to creating Service'))

@@ -436,6 +510,6 @@ }

if (err.statusCode === 409) {
this._app.log.warn(`[k8s] Ingress for project ${project.id} already exists, proceeding...`)
this._app.log.warn(`[k8s] Ingress for instance ${project.id} already exists, proceeding...`)
} else {
if (project.state !== 'suspended') {
this._app.log.error(`[k8s] Project ${project.id} - error creating ingress: ${err.toString()}`)
this._app.log.error(`[k8s] Instance ${project.id} - error creating ingress: ${err.toString()}`)
throw err

@@ -445,2 +519,20 @@ }

}
if (this._customHostname?.enabled) {
const customHostname = await project.getSetting('customHostname')
if (customHostname) {
const customHostnameIngress = await createCustomIngress(project, customHostname, options)
try {
await this._k8sNetApi.createNamespacedIngress(namespace, customHostnameIngress)
} catch (err) {
if (err.statusCode === 409) {
this._app.log.warn(`[k8s] Custom Hostname Ingress for instance ${project.id} already exists, proceeding...`)
} else {
if (project.state !== 'suspended') {
this._app.log.error(`[k8s] Instance ${project.id} - error creating custom hostname ingress: ${err.toString()}`)
throw err
}
}
}
}
}

@@ -458,3 +550,3 @@ await new Promise((resolve, reject) => {

clearInterval(pollInterval)
this._app.log.error(`[k8s] Project ${project.id} - timeout waiting for Ingress`)
this._app.log.error(`[k8s] Instance ${project.id} - timeout waiting for Ingress`)
reject(new Error('Timed out to creating Ingress'))

@@ -492,7 +584,7 @@ }

/**
* Initialises this driver
* @param {string} app - the Vue application
* @param {object} options - A set of configuration options for the driver
* @return {forge.containers.ProjectArguments}
*/
* Initialises this driver
* @param {string} app - the Vue application
* @param {object} options - A set of configuration options for the driver
* @return {forge.containers.ProjectArguments}
*/
init: async (app, options) => {

@@ -509,2 +601,6 @@ this._app = app

this._cloudProvider = this._app.config.driver.options?.cloudProvider
if (this._app.config.driver.options?.customHostname?.enabled) {
this._app.log.info('[k8s] Enabling Custom Hostname Support')
this._customHostname = this._app.config.driver.options?.customHostname
}

@@ -585,3 +681,3 @@ const kc = new k8s.KubeConfig()

this._app.log.error(`[k8s] Error while reading namespaced deployment for project '${project.safeName}' ${project.id}. Error msg=${err.message}, stack=${err.stack}`)
this._app.log.info(`[k8s] Project ${project.id} - recreating deployment`)
this._app.log.info(`[k8s] Instance ${project.id} - recreating deployment`)
const fullProject = await this._app.db.models.Project.byId(project.id)

@@ -597,3 +693,3 @@ await createProject(fullProject, options)

} catch (err) {
this._app.log.debug(`[k8s] Project ${project.id} - recreating deployment`)
this._app.log.debug(`[k8s] Instance ${project.id} - recreating deployment`)
const fullProject = await this._app.db.models.Project.byId(project.id)

@@ -604,3 +700,3 @@ await createProject(fullProject, options)

} catch (err) {
this._app.log.error(`[k8s] Project ${project.id} - error resuming project: ${err.stack}`)
this._app.log.error(`[k8s] Instance ${project.id} - error resuming project: ${err.stack}`)
}

@@ -670,3 +766,3 @@ })

} catch (err) {
this._app.log.error(`[k8s] Project ${project.id} - error deleting ingress: ${err.toString()}`)
this._app.log.error(`[k8s] Instance ${project.id} - error deleting ingress: ${err.toString()}`)
}

@@ -678,6 +774,22 @@

} catch (err) {
this._app.log.error(`[k8s] Project ${project.id} - error deleting tls secret: ${err.toString()}`)
this._app.log.error(`[k8s] Instance ${project.id} - error deleting tls secret: ${err.toString()}`)
}
}
if (this._customHostname?.enabled) {
try {
await this._k8sNetApi.deleteNamespacedIngress(`${project.safeName}-custom`, this._namespace)
} catch (err) {
this._app.log.error(`[k8s] Instance ${project.id} - error deleting custom ingress: ${err.toString()}`)
}
if (this._customHostname?.certManagerIssuer) {
try {
await this._k8sApi.deleteNamespacedSecret(`${project.safeName}-custom`, this._namespace)
} catch (err) {
this._app.log.error(`[k8s] Instance ${project.id} - error deleting custom tls secret: ${err.toString()}`)
}
}
}
// Note that, regardless, the main objective is to delete deployment (runnable)

@@ -699,3 +811,3 @@ // Even if some k8s resources like ingress or service are still not deleted (maybe because of

clearInterval(pollInterval)
this._app.log.error(`[k8s] Project ${project.id} - timed out deleting ingress`)
this._app.log.error(`[k8s] Instance ${project.id} - timed out deleting ingress`)
reject(new Error('Timed out to deleting Ingress'))

@@ -706,3 +818,3 @@ }

} catch (err) {
this._app.log.error(`[k8s] Project ${project.id} - Ingress was not deleted: ${err.toString()}`)
this._app.log.error(`[k8s] Instance ${project.id} - Ingress was not deleted: ${err.toString()}`)
}

@@ -714,3 +826,3 @@

} catch (err) {
this._app.log.error(`[k8s] Project ${project.id} - error deleting service: ${err.toString()}`)
this._app.log.error(`[k8s] Instance ${project.id} - error deleting service: ${err.toString()}`)
}

@@ -731,3 +843,3 @@

clearInterval(pollInterval)
this._app.log.error(`[k8s] Project ${project.id} - timed deleting service`)
this._app.log.error(`[k8s] Instance ${project.id} - timed deleting service`)
reject(new Error('Timed out to deleting Service'))

@@ -738,3 +850,3 @@ }

} catch (err) {
this._app.log.error(`[k8s] Project ${project.id} - Service was not deleted: ${err.toString()}`)
this._app.log.error(`[k8s] Instance ${project.id} - Service was not deleted: ${err.toString()}`)
}

@@ -764,3 +876,3 @@

clearInterval(pollInterval)
this._app.log.error(`[k8s] Project ${project.id} - timed deleting ${pod ? 'Pod' : 'Deployment'}`)
this._app.log.error(`[k8s] Instance ${project.id} - timed deleting ${pod ? 'Pod' : 'Deployment'}`)
reject(new Error('Timed out to deleting Deployment'))

@@ -785,3 +897,3 @@ }

} catch (err) {
this._app.log.error(`[k8s] Project ${project.id} - error deleting ingress: ${err.toString()}`)
this._app.log.error(`[k8s] Instance ${project.id} - error deleting ingress: ${err.toString()}`)
}

@@ -792,5 +904,19 @@ if (this._certManagerIssuer) {

} catch (err) {
this._app.log.error(`[k8s] Project ${project.id} - error deleting tls secret: ${err.toString()}`)
this._app.log.error(`[k8s] Instance ${project.id} - error deleting tls secret: ${err.toString()}`)
}
}
if (this._customHostname?.enabled) {
try {
await this._k8sNetApi.deleteNamespacedIngress(`${project.safeName}-custom`, this._namespace)
} catch (err) {
this._app.log.error(`[k8s] Instance ${project.id} - error deleting custom ingress: ${err.toString()}`)
}
if (this._customHostname?.certManagerIssuer) {
try {
await this._k8sApi.deleteNamespacedSecret(`${project.safeName}-custom`, this._namespace)
} catch (err) {
this._app.log.error(`[k8s] Instance ${project.id} - error deleting custom tls secret: ${err.toString()}`)
}
}
}
try {

@@ -803,3 +929,3 @@ if (project.safeName.match(/^[0-9]/)) {

} catch (err) {
this._app.log.error(`[k8s] Project ${project.id} - error deleting service: ${err.toString()}`)
this._app.log.error(`[k8s] Instance ${project.id} - error deleting service: ${err.toString()}`)
}

@@ -818,5 +944,5 @@ const currentType = await project.getSetting('k8sType')

if (currentType === 'deployment') {
this._app.log.error(`[k8s] Project ${project.id} - error deleting deployment: ${err.toString()}`)
this._app.log.error(`[k8s] Instance ${project.id} - error deleting deployment: ${err.toString()}`)
} else {
this._app.log.error(`[k8s] Project ${project.id} - error deleting pod: ${err.toString()}`)
this._app.log.error(`[k8s] Instance ${project.id} - error deleting pod: ${err.toString()}`)
}

@@ -871,3 +997,3 @@ }

} catch (err) {
this._app.log.debug(`error getting state from project ${project.id}: ${err}`)
this._app.log.debug(`error getting state from instance ${project.id}: ${err}`)
return {

@@ -905,3 +1031,3 @@ id: project.id,

} catch (err) {
this._app.log.debug(`error getting state from project ${project.id}: ${err}`)
this._app.log.debug(`error getting state from instance ${project.id}: ${err}`)
return {

@@ -923,3 +1049,3 @@ id: project.id,

} catch (err) {
this._app.log.debug(`error getting pod status for project ${project.id}: ${err}`)
this._app.log.debug(`error getting pod status for instance ${project.id}: ${err}`)
return {

@@ -1046,3 +1172,3 @@ id: project?.id,

revokeUserToken: async (project, token) => { // logout:nodered(step-3)
this._app.log.debug(`[k8s] Project ${project.id} - logging out node-red instance`)
this._app.log.debug(`[k8s] Instance ${project.id} - logging out node-red instance`)
const endpoints = await getEndpoints(project)

@@ -1049,0 +1175,0 @@ const commands = []

2

package.json
{
"name": "@flowfuse/driver-kubernetes",
"version": "2.4.1-d12c406-202406041336.0",
"version": "2.5.1-6c985ff-202406060934.0",
"description": "Kubernetes driver for FlowFuse",

@@ -5,0 +5,0 @@ "main": "kubernetes.js",

@@ -24,2 +24,7 @@ # FlowForge Docker Container Driver

logPassthrough: true
customHostname:
enabled: true
cnameTarget: custom-loadbalancer.example.com
certManagerIssuer: lets-encrypt
ingressClass: custom-nginx
```

@@ -38,2 +43,7 @@

- `logPassthrough` Have Node-RED logs printed in JSON format to container stdout (default false)
- `customHostname` Settings linked to allowing instances to have a second hostname
- `customHostname.enabled` (default false)
- `customHostname.cnameTarget` The hostname users should configure their DNS entries to point at. Required. (default not set)
- `customHostname.certManagerIssuer` Name of the Cluster issuer to use to create HTTPS certs for the custom hostname (default not set)
- `customHostname.ingressClass` Name of the IngressClass to use to expose the custom hostname (default not set)

@@ -40,0 +50,0 @@ Expects to pick up K8s credentials from the environment

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc