[Solved] Restarting flows programmatically

Since v0.20, the deploy menu has a new Restart Flows option. Would it be possible to provide a way to invoke this programmatically from within a flow? For example, in cases where message properties have been used to override node parameters set when a node was deployed (not encouraged, I realize, but sometimes a good idea), it could be useful to be able to recover the initial state of the flow.

Looks like it simply POSTs to the API function flows

        $.ajax({
            url:"flows",
            type: "POST",
            headers: {
                "Node-RED-Deployment-Type":"reload"
            }
        }).done(function(data,textStatus,xhr) {
            if (deployWasEnabled) {
                $("#btn-deploy").removeClass("disabled");
            }
            RED.notify('<p>'+RED._("deploy.successfulRestart")+'</p>',"success");
        }).fail(function(xhr,textStatus,err) {
            if (deployWasEnabled) {
                $("#btn-deploy").removeClass("disabled");
            }
            if (xhr.status === 401) {
                RED.notify(RED._("deploy.deployFailed",{message:RED._("user.notAuthorized")}),"error");
            } else if (xhr.status === 409) {
                resolveConflict(nns, true);
            } else if (xhr.responseText) {
                RED.notify(RED._("deploy.deployFailed",{message:xhr.responseText}),"error");
            } else {
                RED.notify(RED._("deploy.deployFailed",{message:RED._("deploy.errors.noResponse")}),"error");
            }
        }).always(function() {
            deployInflight = false;
            var delta = Math.max(0,300-(Date.now()-startTime));
            setTimeout(function() {
                $(".deploy-button-content").css('opacity',1);
                $(".deploy-button-spinner").hide();
                $("#header-shade").hide();
                $("#editor-shade").hide();
                $("#palette-shade").hide();
                $("#sidebar-shade").hide();
            },delta);
        });

You can do that yourself:

https://nodered.org/docs/api/admin/methods/post/flows/

1 Like

Brilliant! Thanks a lot. I suspected/hoped that this was available in the Admin API, but I didn't know where to look in the docs. It took me a while to work out the DIY part.

No problem. Just note that I had some problems with jQuery's POST function so I used a raw var request = new XMLHttpRequest() in uibuilder. Though that may be why Nick used $.ajax here rather than $.post(). It is a known bug that triggers a CORS error.

In fact the raw query isn't actually any harder to use than jQuery except that you don't get the promises type interface.

1 Like

Noted. I also had some issues with POST, but it seems to work ok in the particular case I need. If it does go sideways, I'll know what to do.

Hey @TotallyInformation or @drmibell, I'm trying to implement this now, but getting a 400 when I try to actually send the reload to the /flows endpoint. Would either of you have an example working flow you could share?

Sorry, but I can't recover my work on this. I ended by solving my original problem in a completely different way. Perhaps @TotallyInformation can help.

Sorry, my comments were about using jQuery's post function that can have some issues. Not actually needed to do programmatic flow restarts myself.

Anyone ever managed to implement that? I have to do the same exact thing, but not sure how to do it exactly. Thanks!

Hmmm ... I tried to do as proposed but get error 401. I do this:

[{"id":"f60dd315.81c64","type":"function","z":"fe0113d0.6b338","name":"reload flows","func":"//\n// url:\"flows\",\n// type: \"POST\",\n// headers: {\n// \"Node-RED-Deployment-Type\":\"reload\"\n// }\n\nmsg.payload = {headers:{\"Node-RED-Deployment-Type\":\"reload\"}};\n\nreturn msg;","outputs":1,"noerr":0,"x":440,"y":660,"wires":[["e83f1e8f.5db03"]]},{"id":"e83f1e8f.5db03","type":"http request","z":"fe0113d0.6b338","name":"","method":"POST","ret":"txt","paytoqs":false,"url":"localhost:1880/flows","tls":"","persist":false,"proxy":"","authType":"","x":680,"y":660,"wires":[["a409437b.1c6a3"]]},{"id":"2021734a.df7a4c","type":"inject","z":"fe0113d0.6b338","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":240,"y":660,"wires":[["f60dd315.81c64"]]},{"id":"a409437b.1c6a3","type":"debug","z":"fe0113d0.6b338","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":920,"y":660,"wires":[]}]

I finally managed to do this and can programatically restart my flows from within Node Red! Took me a while to work out.

If you are still interested I'll type it up, but I don't want to do it if it's not required any more.

1 Like

i could use your flow to finish my program if you dont mind sharing @droibles

Happily. Just at work now, I'll type it up later. :+1:

2 Likes

thank you!!!

Hi, sorry I didn't get around to this last night.

Here is my flow:

[{"id":"880a338331a21a4d","type":"function","z":"ea6852638fc51d01","name":"Prepare reload flows header","func":"\nvar secret_token = msg.payload.access_token\n\nmsg.payload = {}\nmsg.headers = {}\nmsg.method = \"POST\"\nmsg.url = \"http://nodered.test.com:1880/flows\" ;\n\nmsg.headers[\"Authorization\"] = \"Bearer \" + secret_token\nmsg.headers[\"content-type\"] = \"application/json\"\nmsg.headers[\"Node-RED-Deployment-Type\"] = \"reload\"\n\nreturn msg;\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":460,"wires":[["06bbbe481f4e9155","fb0f73fd1309d014"]]},{"id":"06bbbe481f4e9155","type":"http request","z":"ea6852638fc51d01","name":"Request reload flows","method":"use","ret":"txt","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","senderr":false,"x":1120,"y":520,"wires":[["22e2281626f78481"]]},{"id":"b4d34199e00e0e4b","type":"inject","z":"ea6852638fc51d01","name":"Hourly","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"3600","crontab":"","once":false,"onceDelay":"20","topic":"","payloadType":"date","x":120,"y":400,"wires":[["bdeb53f7f3a9080e"]]},{"id":"22e2281626f78481","type":"debug","z":"ea6852638fc51d01","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1390,"y":520,"wires":[]},{"id":"bdeb53f7f3a9080e","type":"function","z":"ea6852638fc51d01","name":"Pepare auth token header","func":"msg.headers = {}\nmsg.method = \"POST\"\nmsg.url = \"http://nodered.test.com:1880/auth/token\" ;\nmsg.headers[\"content-type\"] = \"application/json\"\n\n\nmsg.payload = {\n\"client_id\":\"node-red-admin\",\n\"grant_type\":\"password\",\n\"scope\":\"*\",\n\"username\":\"your_username\",\n\"password\":\"your_password\"\n}\n\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":330,"y":400,"wires":[["e18a15e1dafaa148"]]},{"id":"e18a15e1dafaa148","type":"http request","z":"ea6852638fc51d01","name":"Request auth token","method":"use","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","senderr":false,"credentials":{},"x":570,"y":400,"wires":[["880a338331a21a4d","a524d01d1ddfb876"]]},{"id":"a524d01d1ddfb876","type":"debug","z":"ea6852638fc51d01","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1410,"y":400,"wires":[]},{"id":"fb0f73fd1309d014","type":"debug","z":"ea6852638fc51d01","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1390,"y":460,"wires":[]}]

I've replaced my NodeRed URL in both of the function nodes msg.url variables with "nodered.test.com" so this will need to be changed to your NodeRed URL.

Also the your_username and your_password in the first function node will need to be replaced with your credentials.

The first function/request gets the auth token which is then used by the second request to restart the flows.

This might not be the most elegant piece of programming but I am no Javascript expert. I'm sure there are better ways of storing the username and password more securely for example.

It does the job for me though. I needed it because somewhere in my setup (I have 24 flow tabs with some fairly complex flows) I have a rogue flow that fails sometimes and pushes my NodeRed process to 100%. So you'll see I actually trigger the restart every hour.

I hope it helps you.

2 Likes

i appreciate it i needed a check down to restart a failing flow...not the best method or fix but if it buys me some time to straighten it out it exactly what i need. Thank you !! i appreciate your flow

No problem. Let me know if it works for you.

1 Like

will be a few days but ill keep you posted.

I've been dealing with some huemagic issues that are causing errors periodically.

So I came up with a slightly different approach that restarts node-red completely. Not sure if that's a good idea or a bad idea, but it works.

You can attach it to a debug node, but I've never seen it output anything, so I deleted it.

What would probably work better is a script to monitor for errors and then automatically trigger the restart, but that's out of my wheelhouse.

[
    {
        "id": "c7d13f9184df6c05",
        "type": "inject",
        "z": "edce5f4c305a1484",
        "name": "Restart Node-RED Every Hour",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "3600",
        "crontab": "",
        "once": false,
        "onceDelay": "3600",
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 330,
        "y": 320,
        "wires": [
            [
                "5beb96fc1c9e261b"
            ]
        ]
    },
    {
        "id": "5beb96fc1c9e261b",
        "type": "exec",
        "z": "edce5f4c305a1484",
        "command": "node-red-restart",
        "addpay": "payload",
        "append": "",
        "useSpawn": "true",
        "timer": "",
        "winHide": false,
        "oldrc": false,
        "name": "",
        "x": 600,
        "y": 320,
        "wires": [
            [],
            [],
            []
        ]
    }
]