npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

direktor

v0.6.0

Published

Executes SSH commands on multiple servers in parallel

Downloads

15

Readme

Direktor: Execute remote tasks via SSH with Node.js

Direktor connects to an arbitrary number of remote machines via SSH and performs one or more commands on them. It uses Brian White's ssh2 library, which is written in pure JavaScript and has no external dependencies.

Individual commands, specified either as strings or closures that are free to interact directly with the SSH session, are grouped in tasks and executed in a strictly serialized manner against a single server. Multiple tasks can be executed in parallel—each in its own SSH session against an arbitrary host—to increase performance.

Example

var dir = require('direktor');

// Create a new session, which manages the overall operations
// against each machine

var session = new dir.Session();

// Create a task, which identifies a group of commands to be
// executed against a given server. The options provided
// are passed directly to the SSH2 library.

var task = new dir.Task({
    host : 'remote1.local',
    port : 22,
    username : 'mine',
    privateKey : require('fs').readFileSync('/home/marco/.ssh/id_rsa')
});

// Add commands to a task

task.before = 'mkdir test';
task.commands = 'echo 1 > test/test.txt';
task.after = 'echo 1 > test/test2.txt';

// Add the task to the session

session.tasks.push(task);

// If you want to run the same set of commands against a different host,
// you can use the clone method:

var newTask = task.clone({
    host : 'remote2.local',
    port : 22,
    username : 'mine',
    privateKey : require('fs').readFileSync('/home/marco/.ssh/id_rsa')
});

session.tasks.push(newTask);

// Execute the tasks. Individual commands are serialized, while
// tasks are parallelized for performance

session.execute(function(err) {
    console.log(err);
});

Tasks

A task is a queue of commands that are executed in a strictly serialized manner against a specific target, which you describe when you instantiate the task:

new Direktor.task(options)

The options parameters is an object and is passed directly to the SSH2 library's connection object. You can connect to a host using password, public key, or any other method supported by SSH2.

A task contains an arbitrary number of commands, which can either be specified as strings, in which case they are executed directly against the SSH session, or as closures, which are passed references to the task object (from whence the SSH session can be derived) and a callback to call when done. For example:

function customCommand(task, callback) {
    task.connection.exec('<remote commands>, function(err) {
        if (err) return callback(err);
        
        // Do your work, then call callback when you're done.
    });
}

Tasks expose four properties, each representing a different phase of their execution:

  • before is a single command or closure that is executed as soon as a connection with the remote host is established.
  • commands is an array of commands or closures that are executed in series right after before. Commands and closures can be mixed as needed.
  • after is a single command or closure that is executed after the last command.
  • error is a single command or closure that is executed if, at any point during the execution of the task, an error is reported by one of the commands. You can use this to attempt to recover from an error gracefully.

All the properties are optional—although, of course, a task without any commands will simply connect to the remote host and disconnect right away.

Handling errors

For string commands, Direktor automatically checks for nonzero exit codes and reports the appropriate error. Closure-style commands are responsible for checking and reporting their own errors.

When an error occurs at any point during a task's execution, the task stops running immediately. If the error command is specified, the tasks executes it, and then closes the connections, reporting the error to the session that owns it.

Cloning tasks

A task can only be executed against the host passed to its constructor. If you want to run the same set of commands against a different host, you can use the clone method after you've populated all of its properties.

Sessions

In order to be executed, tasks must be added to a session, which executes them all in parallel, reporting any errors as appropriate. Session's constructor takes an array of tasks and, optionally, a Bunyan logger (see Logging, below).

In order to execute all the tasks associated with a session, you can call the execute method:

session.execute(finalCallback, stepCallback);

The finalCallback closure is called when all the tasks have finished executing. It receives an array of errors (if any). The stepCallback closure is called every time one task finishes running, and receives a reference to the task and an optional error as its parameters.

Logging

Direktor incorporates Trent Mick's Bunyan for logging. You can specify your own logger by passing it as the second parameter to Session's constructor. If you do not specify a logger, Direktor will create one for you and output logs to the console.