Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Run sequences of shell commands against local and remote hosts.

License

NotificationsYou must be signed in to change notification settings

pstadler/flightplan

Repository files navigation

NPM versionBuild StatusBuild StatusNPM downloadsDependency Status

Run sequences of shell commands against local and remote hosts.

Flightplan is anode.js library for streamlining application deployment or systems administration tasks.

A complete list of changes can be found in theChangelog.

Looking for help / maintainers: See#162.

Installation & Usage

# install the cli tool$ npm install -g flightplan# use it in your project$ npm install flightplan --save-dev# run a flightplan (`fly --help` for more information)$ fly [task:]<target> [--flightplan flightplan.(js|coffee)]

By default, thefly command will try to loadflightplan.js orflightplan.coffee.

Sample flightplan.js

// flightplan.jsvarplan=require('flightplan');// configurationplan.target('staging',{host:'staging.example.com',username:'pstadler',agent:process.env.SSH_AUTH_SOCK});plan.target('production',[{host:'www1.example.com',username:'pstadler',agent:process.env.SSH_AUTH_SOCK},{host:'www2.example.com',username:'pstadler',agent:process.env.SSH_AUTH_SOCK}]);vartmpDir='example-com-'+newDate().getTime();// run commands on localhostplan.local(function(local){local.log('Run build');local.exec('gulp build');local.log('Copy files to remote hosts');varfilesToCopy=local.exec('git ls-files',{silent:true});// rsync files to all the target's remote hostslocal.transfer(filesToCopy,'/tmp/'+tmpDir);});// run commands on the target's remote hostsplan.remote(function(remote){remote.log('Move folder to web root');remote.sudo('cp -R /tmp/'+tmpDir+' ~',{user:'www'});remote.rm('-rf /tmp/'+tmpDir);remote.log('Install dependencies');remote.sudo('npm --production --prefix ~/'+tmpDir+' install ~/'+tmpDir,{user:'www'});remote.log('Reload application');remote.sudo('ln -snf ~/'+tmpDir+' ~/example-com',{user:'www'});remote.sudo('pm2 reload example-com',{user:'www'});});// run more commands on localhost afterwardsplan.local(function(local){/* ... */});// ...or on remote hostsplan.remote(function(remote){/* ... */});

Documentation

Flightplan

A flightplan is a set of subsequent flights to be executed on one or morehosts. Configuration is handled with thetarget() method.

varplan=require('flightplan');

Flights

A flight is a set of commands to be executed on one or more hosts. There aretwo types of flights:

Local flights

Commands in local flights are executed on thelocalhost.

plan.local(function(transport){transport.hostname();// prints the hostname of localhost});

Remote flights

Commands in remote flights are executed inparallel against remote hosts.

plan.remote(function(transport){transport.hostname();// prints the hostname(s) of the remote host(s)});

You can define multiple flights of each type. They will be executed in theorder of their definition. If a previous flight failed, all subsequentflights won't get executed. For more information about what it means fora flight to fail, see the section aboutTransport.

// executed firstplan.local(function(transport){});// executed if first flight succeededplan.remote(function(transport){});// executed if second flight succeededplan.local(function(transport){});// ...

Tasks

Flightplan supports optional tasks to run a subset of flights.

// fly deploy:<target>plan.local('deploy',function(transport){});// fly build:<target>plan.local('build',function(transport){});// fly deploy:<target> or...// fly build:<target>plan.local(['deploy','build'],function(transport){});plan.remote(['deploy','build'],function(transport){});

If no task is specified it's implicitly set to "default". Therefore,fly <target> is the same asfly default:<target>.

// fly <target>plan.local(function(transport){});// is the same as...plan.local('default',function(transport){});// "default" + other tasks:plan.remote(['default','deploy','build'],function(transport){});

flightplan.target(name, hosts[, options]) → this

Configure the flightplan's targets withtarget(). Without aproper setup you can't do remote flights which require atleast one remote host. Each target consists of one or more hosts.

Values in the hosts section are passed directly to theconnect()method ofmscdex/ssh2with one exception:privateKey needs to be passed as a stringcontaining the path to the keyfile instead of the key itself.

// run with `fly staging`plan.target('staging',{// see: https://github.com/mscdex/ssh2#connection-methodshost:'staging.example.com',username:'pstadler',agent:process.env.SSH_AUTH_SOCK});// run with `fly production`plan.target('production',[{host:'www1.example.com',username:'pstadler',agent:process.env.SSH_AUTH_SOCK},{host:'www2.example.com',username:'pstadler',agent:process.env.SSH_AUTH_SOCK}]);// run with `fly dynamic-hosts`plan.target('dynamic-hosts',function(done,runtime){varAWS=require('aws-sdk');AWS.config.update({accessKeyId:'...',secretAccessKey:'...'});varec2=newAWS.EC2();varparams={Filters:[{Name:'instance-state-name',Values:['running']}]};ec2.describeInstances(params,function(err,response){if(err){returndone(err);}varhosts=[];response.data.Reservations.forEach(function(reservation){reservation.Instances.forEach(function(instance){hosts.push({host:instance.PublicIpAddress,username:'pstadler',agent:process.env.SSH_AUTH_SOCK});});});done(hosts);});});

Usually flightplan will abort when a host is not reachable or authenticationfails. This can be prevented by setting a propertyfailsafe totrue onany of the host configurations:

plan.target('production',[{host:'www1.example.com',username:'pstadler',agent:process.env.SSH_AUTH_SOCK},{host:'www2.example.com',username:'pstadler',agent:process.env.SSH_AUTH_SOCK,failsafe:true// continue flightplan even if connection to www2 fails}]);

You can override theusername value of hosts by callingfly withthe-u|--username option:

fly production --username=admin

Configuring remote hosts during runtime (e.g. using AWS/EC2)

Instead of having a static hosts configuration for a target you can configureit on the fly by passing a functionfn(done) as the second argument totarget().

This function is executed at the very beginning. Whatever is passed todone() will be used for connecting to remote hosts. This can either be anobject or an array of objects depending on if you want to connect to one ormultiple hosts. Passing anError object will immediately abort the currentflightplan.

plan.target('dynamic-hosts',function(done,runtime){varAWS=require('aws-sdk');AWS.config.update({accessKeyId:'...',secretAccessKey:'...'});varec2=newAWS.EC2();varparams={Filters:[{Name:'instance-state-name',Values:['running']}]};ec2.describeInstances(params,function(err,response){if(err){returndone(err);}varhosts=[];response.data.Reservations.forEach(function(reservation){reservation.Instances.forEach(function(instance){hosts.push({host:instance.PublicIpAddress,username:'pstadler',agent:process.env.SSH_AUTH_SOCK});});});done(hosts);});});

Defining and using properties depending on the target

target() takes an optional third argument to define properties used bythis target. Values defined in this way can be accessed during runtime.

plan.target('staging',{...},{webRoot:'/usr/local/www',sudoUser:'www'});plan.target('production',{...},{webRoot:'/home/node',sudoUser:'node'});plan.remote(function(remote){varwebRoot=plan.runtime.options.webRoot;// fly staging -> '/usr/local/www'varsudoUser=plan.runtime.options.sudoUser;// fly staging -> 'www'remote.sudo('ls -al '+webRoot,{user:sudoUser});});

Properties can be set and overwritten by passing them as named options to thefly command.

$ fly staging --sudoUser=foo# plan.runtime.options.sudoUser -> 'foo'

flightplan.local([tasks, ]fn) → this

Calling this method registers a local flight. Local flights areexecuted on your localhost. Whenfn gets called aTransport objectis passed with the first argument.

plan.local(function(local){local.echo('hello from your localhost.');});

An optional first parameter of type Array or String can be passed fordefining the flight's task(s).

flightplan.remote([tasks, ]fn) → this

Register a remote flight. Remote flights are executed on the currenttarget's remote hosts defined withtarget(). Whenfn gets calledaTransport object is passed with the first argument.

plan.remote(function(remote){remote.echo('hello from the remote host.');});

An optional first parameter of type Array or String can be passed fordefining the flight's task(s).

flightplan.abort([message])

Manually abort the current flightplan and prevent any further commands andflights from being executed. An optional message can be passed whichis displayed after the flight has been aborted.

plan.abort('Severe turbulences over the atlantic ocean!');

Transport

A transport is the interface you use during flights. Basically theyoffer you a set of methods to execute a chain of commands. Depending on thetype of flight, this is either aShell object for localflights, or anSSH for remote flights. Both transportsexpose the same set of methods as described in this section.

plan.local(function(local){local.echo('Shell.echo() called');});plan.remote(function(remote){remote.echo('SSH.echo() called');});

We call the Transport objecttransport in the following section to avoidconfusion. However, do yourself a favor and uselocal for local, andremote for remote flights.

Accessing runtime information

Flightplan provides information during flights with theruntime properties:

plan.remote(function(transport){// applies to local flights as well// Flightplan specific informationconsole.log(plan.runtime.task);// 'default'console.log(plan.runtime.target);// 'production'console.log(plan.runtime.hosts);// [{ host: 'www1.example.com', port: 22 }, ...]console.log(plan.runtime.options);// { debug: true, ... }// Flight specific informationconsole.log(transport.runtime);// { host: 'www1.example.com', port: 22 }});

transport.exec(command[, options]) → code: int, stdout: String, stderr: String

To execute a command you have the choice between usingexec() or oneof the handy wrappers for often used commands:transport.exec('ls -al') is the same astransport.ls('-al'). If acommand returns a non-zero exit code, the flightplan will be aborted andall subsequent commands and flights won't get executed.

Options

Options can be passed as a second argument. Iffailsafe: true ispassed, the command is allowed to fail (i.e. exiting with a non-zeroexit code), whereassilent: true will simply suppress its output.

// output of `ls -al` is suppressedtransport.ls('-al',{silent:true});// flightplan continues even if command fails with exit code `1`transport.ls('-al foo',{failsafe:true});// ls: foo: No such file or directory// both options togethertransport.ls('-al foo',{silent:true,failsafe:true});

To apply these options to multiple commands check out the docs oftransport.silent() andtransport.failsafe().

Return value

Each command returns an object containingcode,stdout andstderr:

varresult=transport.echo('Hello world');console.log(result);// { code: 0, stdout: 'Hello world\n', stderr: null }

Advanced options

Flightplan useschild_process#exec() for executing local commands andmscdex/ssh2#exec() for remote commands. Options passed withexec willbe forwarded to either of these functions.

// increase maxBuffer for child_process#exec()local.ls('-al',{exec:{maxBuffer:2000*1024}});// enable pty for mscdex/ssh2#exec()remote.ls('-al',{exec:{pty:true}});

transport.sudo(command[, options]) → code: int, stdout: String, stderr: String

Execute a command as another user withsudo(). It has the samesignature asexec(). Per default, the user under which the commandwill be executed is "root". This can be changed by passinguser: "name" with the second argument:

// will run: echo 'echo Hello world' | sudo -u root -i bashtransport.sudo('echo Hello world');// will run echo 'echo Hello world' | sudo -u www -i bashtransport.sudo('echo Hello world',{user:'www'});// further options passed (see `exec()`)transport.sudo('echo Hello world',{user:'www',silent:true,failsafe:true});

Flightplan'ssudo() requires a certain setup on your host. In order tomake things work on a typical Ubuntu installation, follow these rules:

# Scenario:# 'pstadler' is the user for connecting to the host and 'www' is the user# under which you want to execute commands with sudo.# 1. 'pstadler' has to be in the sudo group:$ groups pstadlerpstadler: pstadler sudo# 2. 'pstadler' needs to be able to run sudo -u 'www' without a password.# In order to do this, add the following line to /etc/sudoers:pstadler ALL=(www) NOPASSWD: ALL# 3. user 'www' needs to have a login shell (e.g. bash, sh, zsh, ...)$ cat /etc/passwd| grep wwwwww:x:1002:1002::/home/www:/bin/bash# GOODwww:x:1002:1002::/home/www:/bin/false# BAD

transport.transfer(files, remoteDir[, options]) → [results]

Copy a list of files to the current target's remote host(s) usingrsync with the SSH protocol. File transfers are executed in parallel.After finishing all transfers, an array containing results fromtransport.exec() is returned. This method is only available on localflights.

varfiles=['path/to/file1','path/to/file2'];local.transfer(files,'/tmp/foo');

Files argument

To make things more comfortable, thefiles argument doesn't have to bepassed as an array. Results from previous commands and zero-terminatedstrings are handled as well:

// use result from a previous commandvarfiles=local.git('ls-files',{silent:true});// get list of files under version controllocal.transfer(files,'/tmp/foo');// use zero-terminated result from a previous commandvarfiles=local.exec('(git ls-files -z;find node_modules -type f -print0)',{silent:true});local.transfer(files,'/tmp/foo');// use results from multiple commandsvarresult1=local.git('ls-files',{silent:true}).stdout.split('\n');varresult2=local.find('node_modules -type f',{silent:true}).stdout.split('\n');varfiles=result1.concat(result2);files.push('path/to/another/file');local.transfer(files,'/tmp/foo');

transfer() will use the current host's username defined withtarget() unlessfly is called with the-u|--username option.In this case the latter will be used. If debugging is enabled(either withtarget() or withfly --debug),rsync is executedin verbose mode (-vv).

transport.prompt(message[, options]) → input

Prompt for user input.

varinput=transport.prompt('Are you sure you want to continue? [yes]');if(input.indexOf('yes')===-1){plan.abort('User canceled flight');}// prompt for password (with UNIX-style hidden input)varpassword=transport.prompt('Enter your password:',{hidden:true});// prompt when deploying to a specific targetif(plan.runtime.target==='production'){varinput=transport.prompt('Ready for deploying to production? [yes]');if(input.indexOf('yes')===-1){plan.abort('User canceled flight');}}

transport.waitFor(fn(done)) → {} mixed

Execute a function and return after the callbackdone is called.This is used for running asynchronous functions in a synchronous way.

The callback takes an optional argument which is then returned bywaitFor().

varresult=transport.waitFor(function(done){require('node-notifier').notify({message:'Hello World'},function(err,response){done(err||'sent!');});});console.log(result);// 'sent!'

transport.with(command|options[, options], fn)

Execute commands with a certain context.

transport.with('cd /tmp',function(){transport.ls('-al');// 'cd /tmp && ls -al'});transport.with({silent:true,failsafe:true},function(){transport.ls('-al');// output suppressed, fail safely});transport.with('cd /tmp',{silent:true},function(){transport.ls('-al');// 'cd /tmp && ls -al', output suppressed});

transport.silent()

When callingsilent() all subsequent commands are executed withoutprinting their output to stdout untilverbose() is called.

transport.ls();// output will be printed to stdouttransport.silent();transport.ls();// output won't be printed to stdout

transport.verbose()

Callingverbose() reverts the behavior introduced withsilent().Output of commands will be printed to stdout.

transport.silent();transport.ls();// output won't be printed to stdouttransport.verbose();transport.ls();// output will be printed to stdout

transport.failsafe()

When callingfailsafe(), all subsequent commands are allowed to failuntilunsafe() is called. In other words, the flight will continueeven if the return code of the command is not0. This is helpful ifeither you expect a command to fail or their nature is to return anon-zero exit code.

transport.failsafe();transport.ls('foo');// ls: foo: No such file or directorytransport.log('Previous command failed, but flight was not aborted');

transport.unsafe()

Callingunsafe() reverts the behavior introduced withfailsafe().The flight will be aborted if a subsequent command fails (i.e. returnsa non-zero exit code). This is the default behavior.

transport.failsafe();transport.ls('foo');// ls: foo: No such file or directorytransport.log('Previous command failed, but flight was not aborted');transport.unsafe();transport.ls('foo');// ls: foo: No such file or directory// flight aborted

transport.log(message)

Print a message to stdout. Flightplan takes care that the messageis formatted correctly within the current context.

transport.log('Copying files to remote hosts');

transport.debug(message)

Print a debug message to stdout if debug mode is enabled. Flightplantakes care that the message is formatted correctly within the currentcontext.

transport.debug('Copying files to remote hosts');

About

Run sequences of shell commands against local and remote hosts.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors15


[8]ページ先頭

©2009-2025 Movatter.jp