My requirement is this:
Look for changes in the file /tmp/file
If there is a change, execute these in the following order:
run command3
run command2
run command1
If there is NO change in the file /tmp/file, do nothing.
My code is like this:
exec { 'exec3':
command => 'command3',
require => File['file'],
}
exec { 'exec2':
command => 'command2',
require => Exec['exec3'],
}
exec { 'exec1':
command => 'command1',
require => Exec['exec2'],
subscribe => File['file'],
refreshonly => true,
}
But, whether there is a change to /tmp/file or not, command3 and command2 always runs. How do I prevent it? I do not want "require" to be run in exec1 when there is no change to /tmp/file.
You need: firstly, for all of the execs to subscribe to the file resource; secondly, for each of those to also require their preceding exec resources; and thirdly, for each exec to be set to refreshonly.
Here is some code that does that:
file { 'file':
ensure => file,
path => '/tmp/file',
content => "some content\n",
}
exec { 'exec1':
command => 'command1',
subscribe => File['file'],
refreshonly => true,
}
exec { 'exec2':
command => 'command2',
subscribe => File['file'],
require => Exec['exec1'],
refreshonly => true,
}
exec { 'exec3':
command => 'command3',
subscribe => File['file'],
require => Exec['exec2'],
refreshonly => true,
}
How this works:
Using exec's refreshonly mechanism, exec1 is triggered only on refresh events, and a refresh event is sent if and only if there is a change in file1's content.
All of the exec events need to be similarly triggered by the changes in the file's content, thus they all subscribe to the file.
But the execs need to be ordered in a specific way, and thus exec2 requires exec1, and exec3 requires exec2.
See also reasons why refreshonly needs to be used carefully.
Related
I am creating a cli app using oclif. The user executes a command, and the cli asks him if wants to continue (yes/no answer).
I trying to test the command that uses the cli-ux prompt. I want to simulate the user interaction to enter the 'yes' word.
How can I do that?
I tried this:
describe('mycommand', () => {
test
.stdout()
.command(['mycommand', 'action'])
.stdin('y')
.it('it shoud do someting', ctx => {});
});
Related with Oclif prompt testing I could find a solution.
Be careful how you ask the user because you can use cli.prompt or cli.confirm. In my case, I use cli.confirm so a possible test could be:
describe('it should clean items in done list', () => {
test
.stub(cli, 'confirm', () => async () => 'Y')
.stdout()
.command(['clean', 'done'])
.it('it shoud clean items in done list', ctx => {
// test
});
});
Example of codes that will execute command line using from Node.js. It will return full HTML of page.
const getPageHtmlResponse = (fullUrl) => {//fullUrl is come from input form in web page
return new Promise((resolve, reject) => {
try {
const exec = require('child_process').exec
exec("curl "+fullUrl, (err, stdout, stderr) => resolve(stdout))
} catch (error) {
resolve(false)
}
});
}
Is this code can be insecure? I mean the hackers can inject another command on it to manipulate the system or server?
If yes, there's good way to escape it or make it secure?
Don't use child_process.exec(). A clever crafted string from user input will launch arbitrary code from your program, which you do want to avoid.
Instead, use child_process.execFile() as follows:
const getPageHtmlResponse = (fullUrl) => {//fullUrl is come from input form in web page
return new Promise((resolve, reject) => {
try {
const execFile = require('child_process').execFile
execFile("/path/to/curl", fullUrl, (err, stdout, stderr) => resolve(stdout))
} catch (error) {
resolve(false)
}
});
}
execFile takes the pre-parsed list of commands and does not launch an intermediate shell, so there is less risk of launching a program through an untrusted URL.
See also
child_process.execFile
If the user writes a script and put it on http://blahblah.blah/b and instead of this URL it provides a tricky one: http://blahblah.blah/b | sh, now your code will create a process and execute curl http://blahblah.blah/b | sh. and the script could be anything.
one thing you should consider is that, to validate user input URL, check it to no contain extra commands, and be the only url.
My Scenario
In my node application I'm using child_process.spawn to query information from the current repository
I've built a small function to return a promise which resolves with the response from the command:
const spawn = require('child_process').spawn;
const gitExec = command => (
new Promise((resolve, reject) => {
const thread = spawn('git', command);
const stdOut = [];
const stdErr = [];
thread.stdout.on('data', (data) => {
stdOut.push(data.toString('utf8'));
});
thread.stderr.on('data', (data) => {
stdErr.push(data.toString('utf8'));
});
thread.on('close', () => {
if (stdErr.length) {
reject(stdErr.join(''));
return;
}
resolve(stdOut.join());
});
})
);
module.exports = gitExec;
Calling git branchworks just as expected:
gitExec(['branch'])
.then((branchInfo) => {
console.log(branchInfo);
})
(as expected) results in
* develop
feature/forever
feature/sourceconfig
feature/testing
master
From my understanding this proves the method I'm using to actually be working.
When calling git shortlog -sn the spawned process "hangs" and does not resolve in anything
gitExec(['shortlog', '-sn'])
.then((shortlogInfo) => {
console.log(shortlogInfo);
})
Calling git shortlog -sn via commandline i geht the expected result:
154 Andreas Gack
89 Some other dude
6 Whoever else
My (so far unsuccessfull) tries
using spawnSync (while changing my gitExec function to acommodate the syncronous approach) returns an object as documented - so the process seems to actually exit - but the relevant props of the object output stdout and stderr are all empty.
The status of the object is 0 which indicates the command to execute successfully
I've read about having to redefine the maxBuffer in the spawn options, but neither setting it to a (ridiculously) high value nor a very small value does make a difference in the syncronous or asynchronous approach.
Setting the shell option to true also does not make a difference in all of the above scenarios.
The issue occurrs on my Win10x64 as well as on MacOs running node v6.9.x or 7.x
Also calling the alias git log --pretty=short does not provide a result
My Actual Question
Has anyone yet managed to successfully query git shortlog -sn via child_process.spawn?
Does anyone know a module for Node which allows querying the current local git-repository?
I somehow think the two commands git branch and git shortlog internally handle their output in a different way.
I'd be happy to create an issue on their github page, but I actually don't know how to identify the actual root-cause of that problem.
Any further input is much appreciated!
git shortlog thinks it needs to read something from stdin thats why the whole process hangs. To work around that, you can pass stdin from the main process in as an option and pipe everything else as usual. Then it should run.
const spawn = require('child_process').spawn;
const gitExec = command => (
new Promise((resolve, reject) => {
const thread = spawn('git', command, { stdio: ['inherit', 'pipe', 'pipe'] });
const stdOut = [];
const stdErr = [];
thread.stdout.on('data', (data) => {
stdOut.push(data.toString('utf8'));
});
thread.stderr.on('data', (data) => {
stdErr.push(data.toString('utf8'));
});
thread.on('close', () => {
if (stdErr.length) {
reject(stdErr.join(''));
return;
}
resolve(stdOut.join());
});
})
);
module.exports = gitExec;
Perhaps some more context from the git documentation:
If no revisions are passed on the command line and either standard input is not a terminal or there is no current branch, git shortlog will output a summary of the log read from standard input, without reference to the current repository.
Which is the case when spawning a child process. So it expects something gets passed in via stdin. By setting stdin to the main process git shortlog is aware of the terminal and therefor will run.
I got it working by specifying the before and after commit hashes.
git shortlog -sn `git log --pretty=format:'%H' --reverse | head -1` `git log --pretty=format:'%H' | head -1`"
I have the following class which installs mysql and sets up a user called user but when the create-database commands runs the user has not been created yet.
How do i chain the commands so that the user is created before create-database tries to use it?
class { '::mysql::server':
package_name => 'mariadb-server.x86_64',
root_password => 'root',
remove_default_accounts => true,
override_options => $override_options,
restart => true,
users => {
'user#%' => {
ensure => 'present',
max_connections_per_hour => '0',
max_queries_per_hour => '0',
max_updates_per_hour => '0',
max_user_connections => '0',
password_hash => '...',
}
},
grants => {
'user#%/*.*' => {
ensure => 'present',
options => ['GRANT'],
privileges => ['ALL'],
table => '*.*',
user => 'user#%',
},
}
}->
exec { 'create-database':
creates => '/opt/dbinstalled',
command => '/usr/bin/mysql -u user -puser < /create-db.sql'
}
I am using the puppetlabs-mysql package to install mysql.
You should take a look at the documentation for the require, before, subscribe, notify metaparameters. They are used to describe resource ordering (before, notify), or resource ordering and failure if the dependency fails (require, subscribe). Note the subscribe, notify metaparameters are only available for some resource types (exec, service, etc.).
In this instance, you would do the following to chain a class:
exec { 'create-database':
creates => '/opt/dbinstalled',
command => '/usr/bin/mysql -u user -puser < /create-db.sql',
require => Class[::mysql::server],
}
But you really only need the dependency on the user resource:
exec { 'create-database':
creates => '/opt/dbinstalled',
command => '/usr/bin/mysql -u user -puser < /create-db.sql',
require => User[username or array of users],
}
Also you probably only want to create the database once, so we can give it a subscribe/refreshonly for idempotence:
exec { 'create-database':
creates => '/opt/dbinstalled',
command => '/usr/bin/mysql -u user -puser < /create-db.sql',
subscribe => User[username or array of users],
refreshonly => true,
}
Note that if you change the user resource that the create-database is subscribed to this will rerun the exec resource, so look into the unless, onlyif parameters for exec as other methods to establish idempotence.
is there a way in Puppet to catch a failure when resource is applied, for example, when declaration like
file { '/var/tmp/test':
ensure => file,
mode => '0755',
}
fails, invoke something like
exec { 'Register some failure':
command => '/var/tmp/register failure for /var/tmp/test',
}
?
You can try this :
exec { 'Notify a failure' :
command => "/var/tmp/register failure for /var/tmp/test",
path => "/bin:",
subscribe => File["/var/tmp/test"],
}