Fix for #7431 - reboot didn't work at all previously which is why apps couldn't be published with it enabled, this is now a self host only feature, I've removed the ability to enable a reboot cron in the Cloud and it will not run the lookup/execution.

This commit is contained in:
mike12345567 2022-09-07 17:05:17 +01:00
parent 5bec5dff55
commit dcada36111
8 changed files with 103 additions and 29 deletions

View File

@ -254,8 +254,17 @@ export async function getAllApps({ dev, all, idsOnly, efficient }: any = {}) {
return false
})
if (idsOnly) {
const devAppIds = appDbNames.filter(appId => isDevAppID(appId))
const prodAppIds = appDbNames.filter(appId => !isDevAppID(appId))
switch (dev) {
case true:
return devAppIds
case false:
return prodAppIds
default:
return appDbNames
}
}
const appPromises = appDbNames.map((app: any) =>
// skip setup otherwise databases could be re-created
getAppMetadata(app)

View File

@ -1,6 +1,7 @@
<script>
import { Button, Select, Input, Label } from "@budibase/bbui"
import { createEventDispatcher } from "svelte"
import { onMount, createEventDispatcher } from "svelte"
import { flags } from "stores/backend"
const dispatch = createEventDispatcher()
export let value
@ -29,11 +30,16 @@
label: "Every Night at Midnight",
value: "0 0 * * *",
},
{
]
onMount(() => {
if (!$flags.cloud) {
CRON_EXPRESSIONS.push({
label: "Every Budibase Reboot",
value: "@reboot",
},
]
})
}
})
</script>
<div class="block-field">

View File

@ -125,7 +125,7 @@ async function deployApp(deployment: any) {
const prodAppDoc = await db.get(DocumentType.APP_METADATA)
appDoc._rev = prodAppDoc._rev
} catch (err) {
// ignore the error - doesn't exist
delete appDoc._rev
}
// switch to production app ID

View File

@ -1,16 +1,19 @@
const { processEvent } = require("./utils")
const { queue, shutdown } = require("./bullboard")
const { TRIGGER_DEFINITIONS } = require("./triggers")
const { TRIGGER_DEFINITIONS, rebootTrigger } = require("./triggers")
const { ACTION_DEFINITIONS } = require("./actions")
/**
* This module is built purely to kick off the worker farm and manage the inputs/outputs
*/
exports.init = function () {
exports.init = async function () {
// this promise will not complete
return queue.process(async job => {
const promise = queue.process(async job => {
await processEvent(job)
})
// on init we need to trigger any reboot automations
await rebootTrigger()
return promise
}
exports.getQueues = () => {

View File

@ -9,6 +9,7 @@ const { checkTestFlag } = require("../utilities/redis")
const utils = require("./utils")
const env = require("../environment")
const { doInAppContext, getAppDB } = require("@budibase/backend-core/context")
const { getAllApps } = require("@budibase/backend-core/db")
const TRIGGER_DEFINITIONS = definitions
const JOB_OPTS = {
@ -16,21 +17,24 @@ const JOB_OPTS = {
removeOnFail: true,
}
async function getAllAutomations() {
const db = getAppDB()
let automations = await db.allDocs(
getAutomationParams(null, { include_docs: true })
)
return automations.rows.map(row => row.doc)
}
async function queueRelevantRowAutomations(event, eventType) {
if (event.appId == null) {
throw `No appId specified for ${eventType} - check event emitters.`
}
doInAppContext(event.appId, async () => {
const db = getAppDB()
let automations = await db.allDocs(
getAutomationParams(null, { include_docs: true })
)
let automations = await getAllAutomations()
// filter down to the correct event type
automations = automations.rows
.map(automation => automation.doc)
.filter(automation => {
automations = automations.filter(automation => {
const trigger = automation.definition.trigger
return trigger && trigger.event === eventType
})
@ -110,4 +114,34 @@ exports.externalTrigger = async function (
}
}
exports.rebootTrigger = async () => {
// reboot cron option is only available on the main thread at
// startup and only usable in self host
if (env.isInThread() || !env.SELF_HOSTED) {
return
}
// iterate through all production apps, find the reboot crons
// and trigger events for them
const appIds = await getAllApps({ dev: false, idsOnly: true })
for (let prodAppId of appIds) {
await doInAppContext(prodAppId, async () => {
let automations = await getAllAutomations()
let rebootEvents = []
for (let automation of automations) {
if (utils.isRebootTrigger(automation)) {
const job = {
automation,
event: {
appId: prodAppId,
timestamp: Date.now(),
},
}
rebootEvents.push(queue.add(job, JOB_OPTS))
}
}
await Promise.all(rebootEvents)
})
}
}
exports.TRIGGER_DEFINITIONS = TRIGGER_DEFINITIONS

View File

@ -17,6 +17,7 @@ import { tenancy } from "@budibase/backend-core"
import { quotas } from "@budibase/pro"
import { Automation } from "@budibase/types"
const REBOOT_CRON = "@reboot"
const WH_STEP_ID = definitions.WEBHOOK.stepId
const CRON_STEP_ID = definitions.CRON.stepId
const Runner = new Thread(ThreadType.AUTOMATION)
@ -109,22 +110,33 @@ export async function clearMetadata() {
await db.bulkDocs(automationMetadata)
}
/**
* This function handles checking of any cron jobs that need to be enabled/updated.
* @param {string} appId The ID of the app in which we are checking for webhooks
* @param {object|undefined} automation The automation object to be updated.
*/
export async function enableCronTrigger(appId: any, automation: any) {
const trigger = automation ? automation.definition.trigger : null
function isCronTrigger(auto: any) {
export function isCronTrigger(auto: Automation) {
return (
auto &&
auto.definition.trigger &&
auto.definition.trigger.stepId === CRON_STEP_ID
)
}
export function isRebootTrigger(auto: Automation) {
const trigger = auto ? auto.definition.trigger : null
return isCronTrigger(auto) && trigger?.inputs.cron === REBOOT_CRON
}
/**
* This function handles checking of any cron jobs that need to be enabled/updated.
* @param {string} appId The ID of the app in which we are checking for webhooks
* @param {object|undefined} automation The automation object to be updated.
*/
export async function enableCronTrigger(appId: any, automation: Automation) {
const trigger = automation ? automation.definition.trigger : null
// need to create cron job
if (isCronTrigger(automation) && trigger?.inputs.cron) {
if (
isCronTrigger(automation) &&
!isRebootTrigger(automation) &&
trigger?.inputs.cron
) {
// make a job id rather than letting Bull decide, makes it easier to handle on way out
const jobId = `${appId}_cron_${newid()}`
const job: any = await queue.add(

View File

@ -458,6 +458,9 @@ class Orchestrator {
export function execute(input: AutomationEvent, callback: WorkerCallback) {
const appId = input.data.event.appId
if (!appId) {
throw new Error("Unable to execute, event doesn't contain app ID.")
}
doInAppContext(appId, async () => {
const automationOrchestrator = new Orchestrator(
input.data.automation,
@ -475,6 +478,9 @@ export function execute(input: AutomationEvent, callback: WorkerCallback) {
export const removeStalled = async (input: AutomationEvent) => {
const appId = input.data.event.appId
if (!appId) {
throw new Error("Unable to execute, event doesn't contain app ID.")
}
await doInAppContext(appId, async () => {
const automationOrchestrator = new Orchestrator(
input.data.automation,

View File

@ -25,6 +25,10 @@ export interface AutomationStep {
export interface AutomationTrigger {
id: string
stepId: string
inputs: {
[key: string]: any
}
cronJobId?: string
}
export enum AutomationStatus {